]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5209 SCM, coverage time changes
authorStas Vilchik <vilchiks@gmail.com>
Wed, 11 Jun 2014 05:30:59 +0000 (11:30 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 11 Jun 2014 05:30:59 +0000 (11:30 +0600)
21 files changed:
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-server/src/main/coffee/component-viewer/header.coffee
sonar-server/src/main/coffee/component-viewer/header/coverage-header.coffee
sonar-server/src/main/coffee/component-viewer/header/issues-header.coffee
sonar-server/src/main/coffee/component-viewer/header/scm-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/main.coffee
sonar-server/src/main/coffee/component-viewer/mixins/main-coverage.coffee
sonar-server/src/main/coffee/component-viewer/mixins/main-duplications.coffee
sonar-server/src/main/coffee/component-viewer/mixins/main-issues.coffee
sonar-server/src/main/coffee/component-viewer/mixins/main-scm.coffee
sonar-server/src/main/coffee/component-viewer/source.coffee
sonar-server/src/main/coffee/component-viewer/time-changes-popup.coffee
sonar-server/src/main/hbs/component-viewer/header.hbs
sonar-server/src/main/hbs/component-viewer/header/coverage-header.hbs
sonar-server/src/main/hbs/component-viewer/header/issues-header.hbs
sonar-server/src/main/hbs/component-viewer/header/scm-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/source.hbs
sonar-server/src/main/hbs/component-viewer/time-changes-popup.hbs
sonar-server/src/main/js/common/handlebars-extensions.js
sonar-server/src/main/js/translate.js
sonar-server/src/main/less/component-viewer.less

index 6eaf0e4daa02e7ac316aa776f8154a94c20279c3..c5e1da579dd9833b81f058abfbe7f84960b5daf8 100644 (file)
@@ -2668,6 +2668,7 @@ component_viewer.header.debt=Debt
 component_viewer.header.toggle_issues=Toggle issues
 component_viewer.header.toggle_coverage=Toggle coverage
 component_viewer.header.toggle_duplications=Toggle duplications
+component_viewer.header.toggle_scm=Toggle SCM
 
 component_viewer.transition.coverage=Covered By
 component_viewer.transition.covers=Covers
index b6f84d4ba0f5a302d38551ec8a791ac4265a4e4e..f0487c4b8facc4272a13c0c461512361c51b53f4 100644 (file)
@@ -6,6 +6,7 @@ define [
   'component-viewer/header/issues-header'
   'component-viewer/header/coverage-header'
   'component-viewer/header/duplications-header'
+  'component-viewer/header/scm-header'
   'component-viewer/header/tests-header'
 
   'common/handlebars-extensions'
@@ -17,6 +18,7 @@ define [
   IssuesHeaderView
   CoverageHeaderView
   DuplicationsHeaderView
+  SCMHeaderView
   TestsHeaderView
 ) ->
 
@@ -28,6 +30,7 @@ define [
     { scope: 'issues', view: IssuesHeaderView }
     { scope: 'coverage', view: CoverageHeaderView }
     { scope: 'duplications', view: DuplicationsHeaderView }
+    { scope: 'scm', view: SCMHeaderView }
     { scope: 'tests', view: TestsHeaderView }
   ]
 
index 39f8f8e81893398cbb60a8da1a9e62d672dfea7a..ecf1b4b0618cf14c1ee982b45a52a5525ce6979f 100644 (file)
@@ -2,18 +2,24 @@ define [
   'backbone.marionette'
   'templates/component-viewer'
   'component-viewer/header/base-header'
+  'component-viewer/time-changes-popup'
 ], (
   Marionette
   Templates
   BaseHeaderView
+  TimeChangesPopupView
 ) ->
 
+  $ = jQuery
+
 
   class extends BaseHeaderView
     template: Templates['coverage-header']
 
 
     events:
+      'click .js-coverage-time-changes': 'coverageTimeChanges'
+
       'click .js-filter-lines-to-cover': 'filterByLinesToCover'
       'click .js-filter-uncovered-lines': 'filterByUncoveredLines'
       'click .js-filter-branches-to-cover': 'filterByBranchesToCover'
@@ -24,16 +30,56 @@ define [
       'click .js-filter-uncovered-branches-it': 'filterByUncoveredBranchesIT'
 
 
-    filterByLinesToCover: (e) -> @header.filterLines e, 'filterByLinesToCover'
-    filterByCoveredLines: (e) -> @header.filterLines e, 'filterByCoveredLines'
-    filterByUncoveredLines: (e) -> @header.filterLines e, 'filterByUncoveredLines'
-    filterByBranchesToCover: (e) -> @header.filterLines e, 'filterByBranchesToCover'
-    filterByCoveredBranches: (e) -> @header.filterLines e, 'filterByCoveredBranches'
-    filterByUncoveredBranches: (e) -> @header.filterLines e, 'filterByUncoveredBranches'
+    coverageTimeChanges: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      popup = new TimeChangesPopupView
+        triggerEl: $(e.currentTarget)
+        main: @options.main
+        bottom: true
+      popup.render()
+      popup.on 'change', (period) => @main.enableSCMPeriod period
+
+
+    filterByLinesToCover: (e) ->
+      @header.filterLines e, 'filterByLinesToCover'
+      @state.set 'activeHeaderItem', '.js-filter-lines-to-cover'
+
+
+    filterByUncoveredLines: (e) ->
+      @header.filterLines e, 'filterByUncoveredLines'
+      @state.set 'activeHeaderItem', '.js-filter-uncovered-lines'
+
+
+    filterByBranchesToCover: (e) ->
+      @header.filterLines e, 'filterByBranchesToCover'
+      @state.set 'activeHeaderItem', '.js-filter-branches-to-cover'
+
+
+    filterByUncoveredBranches: (e) ->
+      @header.filterLines e, 'filterByUncoveredBranches'
+      @state.set 'activeHeaderItem', '.js-filter-uncovered-branches'
+
+
+    filterByLinesToCoverIT: (e) ->
+      @header.filterLines e, 'filterByLinesToCoverIT'
+      @state.set 'activeHeaderItem', '.js-filter-lines-to-cover-it'
+
+
+    filterByUncoveredLinesIT: (e) ->
+      @header.filterLines e, 'filterByUncoveredLinesIT'
+      @state.set 'activeHeaderItem', '.js-filter-uncovered-lines-it'
+
+
+    filterByBranchesToCoverIT: (e) ->
+      @header.filterLines e, 'filterByBranchesToCoverIT'
+      @state.set 'activeHeaderItem', '.js-filter-branches-to-cover-it'
+
+
+    filterByUncoveredBranchesIT: (e) ->
+      @header.filterLines e, 'filterByUncoveredBranchesIT'
+      @state.set 'activeHeaderItem', '.js-filter-uncovered-branches-it'
+
 
-    filterByLinesToCoverIT: (e) -> @header.filterLines e, 'filterByLinesToCoverIT'
-    filterByCoveredLinesIT: (e) -> @header.filterLines e, 'filterByCoveredLinesIT'
-    filterByUncoveredLinesIT: (e) -> @header.filterLines e, 'filterByUncoveredLinesIT'
-    filterByBranchesToCoverIT: (e) -> @header.filterLines e, 'filterByBranchesToCoverIT'
-    filterByCoveredBranchesIT: (e) -> @header.filterLines e, 'filterByCoveredBranchesIT'
-    filterByUncoveredBranchesIT: (e) -> @header.filterLines e, 'filterByUncoveredBranchesIT'
\ No newline at end of file
+    serializeData: ->
+      _.extend super, period: @state.get('period')?.toJSON()
\ No newline at end of file
index 13989aa78d15710ef96a4021e39172fc5d100112..f9b20516a87cc922722a343c4547c7b7b0c72eb5 100644 (file)
@@ -48,6 +48,7 @@ define [
         triggerEl: $(e.currentTarget)
         main: @options.main
         bottom: true
+        prefix: 'Added'
       popup.render()
       popup.on 'change', (period) => @main.enableIssuesPeriod period
 
diff --git a/sonar-server/src/main/coffee/component-viewer/header/scm-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/scm-header.coffee
new file mode 100644 (file)
index 0000000..a44ad64
--- /dev/null
@@ -0,0 +1,36 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+  'component-viewer/time-changes-popup'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+  TimeChangesPopupView
+) ->
+
+  $ = jQuery
+
+
+  class extends BaseHeaderView
+    template: Templates['scm-header']
+
+
+    events:
+      'click .js-scm-time-changes': 'scmTimeChanges'
+
+
+    scmTimeChanges: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      popup = new TimeChangesPopupView
+        triggerEl: $(e.currentTarget)
+        main: @options.main
+        bottom: true
+      popup.render()
+      popup.on 'change', (period) => @main.enableSCMPeriod period
+
+
+    serializeData: ->
+      _.extend super, period: @state.get('period')?.toJSON()
index ada2780e0a0b59bdfa07b6c8c485db04894823ea..8d940e5b5197df732d48b14015907884f4871d21 100644 (file)
@@ -50,6 +50,11 @@ define [
     'complexity,function_complexity,' +
     'comment_lines,comment_lines_density,public_api,public_undocumented_api,public_documented_api_density'
 
+  NEW_SOURCE_METRIC_LIST = 'new_accessors,classes,functions,statements,' +
+      'ncloc,lines,' +
+      'complexity,function_complexity,' +
+      'comment_lines,comment_lines_density,public_api,public_undocumented_api,public_documented_api_density'
+
   COVERAGE_METRIC_LIST = 'coverage,line_coverage,lines_to_cover,covered_lines,uncovered_lines,' +
     'branch_coverage,conditions_to_cover,uncovered_conditions,' +
     'it_coverage,it_line_coverage,it_lines_to_cover,it_covered_lines,it_uncovered_lines,' +
@@ -150,15 +155,19 @@ define [
         if full
           # Periods
           @periods.reset [{}]
-          data.periods.forEach (p) => @periods.add key: p[0], label: p[1], sinceDate: new Date p[2]
+          data.periods.forEach (p) =>
+            d = new Date p[2]
+            d.setHours 0, 0, 0, 0
+            p = @periods.add key: p[0], label: p[1], sinceDate: d
 
 
-    requestMeasures: (key) ->
+    requestMeasures: (key, period = null) ->
       @state.set 'hasMeasures', true
+      return @requestTrends(key, period) if period?
       unless @component.get 'isUnitTest'
         metrics = [SOURCE_METRIC_LIST, COVERAGE_METRIC_LIST, ISSUES_METRIC_LIST, DUPLICATIONS_METRIC_LIST].join ','
       else
-        metrics = [ISSUES_METRIC_LIST, TESTS_METRIC_LIST]
+        metrics = [ISSUES_METRIC_LIST, TESTS_METRIC_LIST].join ','
       data = resource: key, metrics: metrics
       $.get API_MEASURES, data, (data) =>
         measuresList = data[0].msr || []
@@ -168,10 +177,30 @@ define [
         @component.set 'measures', measures
 
 
+    requestTrends: (key, period) ->
+      unless @component.get 'isUnitTest'
+        metrics = COVERAGE_METRIC_LIST
+      else
+        metrics = ''
+      metrics = metrics.split(',').map((m) -> "new_#{m}").join ','
+      data = resource: key, metrics: metrics, includetrends: true
+      $.get API_MEASURES, data, (data) =>
+        measuresList = data[0].msr || []
+        measures = @component.get 'measures'
+        measuresList.forEach (m) ->
+          key = m.key.substr(4)
+          variation = "var#{period}"
+          measures[key] = m[variation]
+        @component.set 'measures', measures
+
+
     requestSource: (key) ->
       $.get API_SOURCES, key: key, (data) =>
         @source.clear()
-        @source.set source: data.sources
+        formattedSource = _.map data.sources, (item) => lineNumber: item[0], code: item[1]
+        @source.set
+          source: data.sources
+          formattedSource: formattedSource
 
 
     requestTests: (key) ->
index 88b5bb35b4fec76cb37536e1fa17c9bcd32e4a2f..c252c3d1f16c45d606b58b260bf1f64aeb830f37 100644 (file)
@@ -9,8 +9,26 @@ define [], () ->
 
     requestCoverage: (key, type = 'UT') ->
       $.get API_COVERAGE, key: key, type: type, (data) =>
+        return unless data?.coverage?
         @state.set 'hasCoverage', true
         @source.set coverage: data.coverage
+        @augmentWithCoverage data.coverage
+
+
+    augmentWithCoverage: (coverage) ->
+      formattedSource = @source.get 'formattedSource'
+      coverage.forEach (c) ->
+        line = _.findWhere formattedSource, lineNumber: c[0]
+        line.coverage =
+          covered: c[1]
+          testCases: c[2]
+          branches: c[3]
+          coveredBranches: c[4]
+        if line.coverage.branches? && line.coverage.coveredBranches?
+          line.coverage.branchCoverageStatus = 'green' if line.coverage.branches == line.coverage.coveredBranches
+          line.coverage.branchCoverageStatus = 'orange' if line.coverage.branches > line.coverage.coveredBranches
+          line.coverage.branchCoverageStatus = 'red' if line.coverage.coveredBranches == 0
+      @source.set 'formattedSource', formattedSource
 
 
     showCoverage: (store = false) ->
@@ -37,24 +55,54 @@ define [], () ->
 
 
     _filterByCoverage: (predicate) ->
-      coverage = @source.get 'coverage'
+      period = @state.get('period')
+      if period
+        periodDate = period.get 'sinceDate'
+        p = predicate
+        predicate = (line) =>
+          line?.scm?.date? && (new Date(line.scm.date) >= periodDate) && p(line)
+
+      formattedSource = @source.get 'formattedSource'
       @settings.set 'coverage', true
       @sourceView.resetShowBlocks()
-      coverage.forEach (c) =>
-        if predicate c
-          line = c[0]
-          @sourceView.addShowBlock line - LINES_AROUND_COVERED_LINE, line + LINES_AROUND_COVERED_LINE
+      formattedSource.forEach (line) =>
+        if predicate line
+          ln = line.lineNumber
+          @sourceView.addShowBlock ln - LINES_AROUND_COVERED_LINE, ln + LINES_AROUND_COVERED_LINE
       @sourceView.render()
 
 
     # Unit Tests
-    filterByLinesToCover: -> @filterByCoverage (c) -> c[1]?
-    filterByUncoveredLines: -> @filterByCoverage (c) -> c[1]? && !c[1]
-    filterByBranchesToCover: -> @filterByCoverage (c) -> c[3]?
-    filterByUncoveredBranches: -> @filterByCoverage (c) -> c[3]? && c[4]? && (c[3] > c[4])
+    filterByLinesToCover: ->
+      @filterByCoverage (line) -> line?.coverage?.covered?
+
+
+    filterByUncoveredLines: ->
+      @filterByCoverage (line) -> line?.coverage?.covered? && !line.coverage.covered
+
+
+    filterByBranchesToCover: ->
+      @filterByCoverage (line) -> line?.coverage?.branches?
+
+
+    filterByUncoveredBranches: ->
+      @filterByCoverage (line) -> line?.coverage?.branches? && line.coverage.coveredBranches? &&
+          line.coverage.branches > line.coverage.coveredBranches
+
 
     # Integration Tests
-    filterByLinesToCoverIT: -> @filterByCoverageIT (c) -> c[1]?
-    filterByUncoveredLinesIT: -> @filterByCoverageIT (c) -> c[1]? && !c[1]
-    filterByBranchesToCoverIT: -> @filterByCoverageIT (c) -> c[3]?
-    filterByUncoveredBranchesIT: -> @filterByCoverageIT (c) -> c[3]? && c[4]? && (c[3] > c[4])
\ No newline at end of file
+    filterByLinesToCoverIT: ->
+      @filterByCoverageIT (line) -> line?.coverage?.covered?
+
+
+    filterByUncoveredLinesIT: ->
+      @filterByCoverageIT (line) -> line?.coverage?.covered? && !line.coverage.covered
+
+
+    filterByBranchesToCoverIT: ->
+      @filterByCoverageIT (line) -> line?.coverage?.branches?
+
+
+    filterByUncoveredBranchesIT: ->
+      @filterByCoverageIT (line) -> line?.coverage?.branches? && line.coverage.coveredBranches? &&
+          line.coverage.branches > line.coverage.coveredBranches
\ No newline at end of file
index 6be975159518504cdd57d35a40e846da7fa4d685..93d004abfec9104a013ad7852e34301551f4c4c0 100644 (file)
@@ -9,9 +9,27 @@ define [], () ->
 
     requestDuplications: (key) ->
       $.get API_DUPLICATIONS, key: key, (data) =>
+        return unless data?.duplications?
         @state.set 'hasDuplications', true
         @source.set duplications: data.duplications
         @source.set duplicationFiles: data.files
+        @augmentWithDuplications data.duplications
+
+
+    augmentWithDuplications: (duplications) ->
+      formattedSource = @source.get 'formattedSource'
+      formattedSource.forEach (line) ->
+        lineDuplications = []
+        duplications.forEach (d, i) ->
+          duplicated = false
+          d.blocks.forEach (b) ->
+            if b._ref == '1'
+              lineFrom = b.from
+              lineTo = b.from + b.size
+              duplicated = true if line.lineNumber >= lineFrom && line.lineNumber <= lineTo
+          lineDuplications.push if duplicated then i + 1 else false
+        line.duplications = lineDuplications
+      @source.set 'formattedSource', formattedSource
 
 
     showDuplications: (store = false) ->
index d29235bc9ca8cd535e687e8d23ea13b402cb0611..c396afa3c6c3e6ccf44910579994a1358606775e 100644 (file)
@@ -37,7 +37,9 @@ define [], () ->
 
 
     requestIssuesPeriod: (key, period) ->
-      $.get API_COMPONENT, key: key, period: period, (data) =>
+      params = key: key
+      params.period = period if period?
+      $.get API_COMPONENT, params, (data) =>
         rules = data.rules.map (r) -> key: r[0], name: r[1], count: r[2]
         severities = data.severities.map (r) -> key: r[0], name: r[1], count: r[2]
         @state.set rules: rules, severities: severities
@@ -46,10 +48,8 @@ define [], () ->
     enableIssuesPeriod: (periodKey) ->
       period = if periodKey == '' then null else @periods.findWhere key: periodKey
       @state.set 'issuesPeriod', period
-      if period?
-        @requestIssuesPeriod(@key, period.get('key')).done => @headerView.render()
-      else
-        @requestComponent(@key, false, false).done => @headerView.render()
+      periodKey = if period? then period.get 'key' else null
+      @requestIssuesPeriod(@key, periodKey).done => @headerView.render()
 
 
     filterLinesByIssues: ->
index 3e92b7528b50da5e441f0393232e3d6d07690883..57833917494f02d73a4c73d57c8808e53e06a28b 100644 (file)
@@ -8,8 +8,28 @@ define [], () ->
 
     requestSCM: (key) ->
       $.get API_SCM, key: key, (data) =>
-        @state.set 'hasSCM', true
-        @source.set scm: data.scm
+        if data?.scm?
+          @state.set 'hasSCM', true
+          @source.set scm: data.scm
+          @augmentWithSCM data.scm
+
+
+    augmentWithSCM: (scm) ->
+      formattedSource = @source.get 'formattedSource'
+      scmLength = scm.length
+      if scmLength > 0
+        scmIndex = 0
+        scmCurrent = scm[scmIndex]
+        scmDetails = {}
+        formattedSource.forEach (line) ->
+          if line.lineNumber == scmCurrent[0]
+            scmDetails = author: scmCurrent[1], date: scmCurrent[2]
+            if scmIndex < scmLength - 1
+              scmIndex++
+              scmCurrent = scm[scmIndex]
+          line.scm = scmDetails
+        @source.set 'formattedSource', formattedSource
+
 
 
     showSCM: (store = false) ->
@@ -24,4 +44,40 @@ define [], () ->
     hideSCM: (store = false) ->
       @settings.set 'scm', false
       @storeSettings() if store
-      @sourceView.render()
\ No newline at end of file
+      @sourceView.render()
+
+
+    filterBySCM: ->
+      @requestSCM(@key).done => @_filterBySCM()
+
+
+    _filterBySCM: () ->
+      formattedSource = @source.get 'formattedSource'
+      period = @state.get 'period'
+      unless period?
+        return @showAllLines()
+      else
+        periodDate = period.get 'sinceDate'
+      @settings.set 'scm', true
+      @sourceView.resetShowBlocks()
+      scmBlockLine = 1
+      predicate = false
+      formattedSource.forEach (line) =>
+        scmBlockDate = new Date line.scm.date
+        if scmBlockDate >= periodDate
+          scmBlockLine = line.lineNumber if predicate == false
+          predicate = true
+        else if predicate == true
+          predicate = false
+          @sourceView.addShowBlock scmBlockLine, line.lineNumber - 1
+      if predicate
+        @sourceView.addShowBlock scmBlockLine, _.size @source.get 'source'
+      @sourceView.render()
+
+
+    enableSCMPeriod: (periodKey) ->
+      period = if periodKey == '' then null else @periods.findWhere key: periodKey
+      @state.set 'period', period
+      @requestMeasures(@key, period?.get('key')).done =>
+        @headerView.render()
+        @filterBySCM() unless @state.get('activeHeaderItem')
\ No newline at end of file
index 0ace43ecefbaa4e8221f2e207dedff8bbef345c7..2e1b4d8a6770c996b4b878b219c3e1f3c87e7aa1 100644 (file)
@@ -174,40 +174,6 @@ define [
       @options.main.showAllLines()
 
 
-    augmentWithCoverage: (source) ->
-      coverage = @model.get 'coverage'
-      if coverage
-        coverage.forEach (s) ->
-          line = source[s[0] - 1]
-          line.coverage =
-            covered: s[1]
-            testCases: s[2]
-            branches: s[3]
-            coveredBranches: s[4]
-          if line.coverage.branches? && line.coverage.coveredBranches?
-            line.coverage.branchCoverageStatus = 'green' if line.coverage.branches == line.coverage.coveredBranches
-            line.coverage.branchCoverageStatus = 'orange' if line.coverage.branches > line.coverage.coveredBranches
-            line.coverage.branchCoverageStatus = 'red' if line.coverage.coveredBranches == 0
-      source
-
-
-    augmentWithDuplications: (source) ->
-      duplications = @model.get 'duplications'
-      return source unless duplications?
-      source.forEach (line) ->
-        lineDuplications = []
-        duplications.forEach (d, i) ->
-          duplicated = false
-          d.blocks.forEach (b) ->
-            if b._ref == '1'
-              lineFrom = b.from
-              lineTo = b.from + b.size
-              duplicated = true if line.lineNumber >= lineFrom && line.lineNumber <= lineTo
-          lineDuplications.push if duplicated then i + 1 else false
-        line.duplications = lineDuplications
-      source
-
-
     getSCMForLine: (lineNumber) ->
       scm = @model.get('scm') || []
       closest = -1
@@ -244,18 +210,9 @@ define [
 
 
     prepareSource: ->
-      source = @model.get 'source'
-      source = _.map source, (item) =>
-        lineNumber: item[0], code: item[1]
-
-      if @options.main.settings.get 'coverage'
-        source = @augmentWithCoverage source
-      if @options.main.settings.get 'duplications'
-        source = @augmentWithDuplications source
-      if @options.main.settings.get 'scm'
-        source = @augmentWithSCM source
-
-      @augmentWithShow source
+      source = @model.get 'formattedSource'
+      if source?
+        @augmentWithShow source
 
 
     getStatColumnsCount: ->
index a677ee7dd5b5c4b1953a6b4e57d2fe4d6123d347..2e1cad33c7257e89085a0f9a161e152ef52eddc2 100644 (file)
@@ -27,3 +27,4 @@ define [
     serializeData: ->
       component: @options.main.component.toJSON()
       periods: @options.main.periods.toJSON()
+      prefix: @options.prefix || 'Δ'
index a3733bf8285735ccbcb134af4df1d5db8da0e350..c20b5896ed8b6164e1535d22d3c9a8ac6d561f8e 100644 (file)
 
     {{#inArray state.tabs 'scm'}}
       <div class="component-viewer-header-measures-scope">
-        <span data-scope="scm" class="component-viewer-header-measures-expand">
+        <a data-scope="scm" class="component-viewer-header-measures-expand">
           <div class="component-viewer-header-measure">
             <span class="component-viewer-header-measure-value"><i class="icon-calendar"></i></span>
             <span class="component-viewer-header-measure-label">SCM</span>
           </div>
-        </span>
-        <a data-scope="scm" title="{{t 'component_viewer.header.toggle_duplications'}}"
+          <i class="icon-dropdown"></i>
+        </a>
+        <a data-scope="scm" title="{{t 'component_viewer.header.toggle_scm'}}"
            class="js-toggle-scm component-viewer-header-measures-toggle-scope {{#if settings.scm}}active{{/if}}"></a>
       </div>
     {{/inArray}}
index 2e2e4a492b51a04764b4e5e392339f4b43781cae..c2b0a87ab817bbf2563aab00233d8be02fc4ebec 100644 (file)
@@ -1,52 +1,63 @@
-{{#with component.measures}}
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.unit_tests'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderItem coverage 'coverage'}}}
-      {{{componentViewerHeaderItem line_coverage 'line_coverage'}}}
-      {{{componentViewerHeaderLink lines_to_cover 'lines_to_cover' 'js-filter-lines-to-cover'}}}
-      {{{componentViewerHeaderLink uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines'}}}
-    </ul>
-  </div>
+<div class="component-viewer-header-time-changes">
+  <a class="highlighted-link js-coverage-time-changes">
+    {{#if period}}Δ {{period.label}}{{else}}<i class="icon-period"></i> Time Changes{{/if}}
+  </a>
+</div>
 
-  {{#any branch_coverage conditions_to_cover covered_conditions uncovered_conditions}}
-    <div class="component-viewer-header-expanded-bar-section">
-      <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
-      <ul class="component-viewer-header-expanded-bar-section-list">
-        <li><span class="item">&nbsp;</span></li>
-        {{{componentViewerHeaderItem branch_coverage 'branch_coverage'}}}
-        {{{componentViewerHeaderLink conditions_to_cover 'conditions_to_cover' 'js-filter-branches-to-cover'}}}
-        {{{componentViewerHeaderLink uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches'}}}
-      </ul>
-    </div>
-  {{/any}}
+{{#with component.measures}}
+  <span class="nowrap">
+    {{#any coverage line_coverage lines_to_cover covered_lines uncovered_lines}}
+      <div class="component-viewer-header-expanded-bar-section">
+        <div class="component-viewer-header-expanded-bar-section-title">
+          {{t 'component_viewer.measure_section.unit_tests'}}
+        </div>
+        <ul class="component-viewer-header-expanded-bar-section-list">
+          {{{componentViewerHeaderItem coverage 'coverage'}}}
+          {{{componentViewerHeaderItem line_coverage 'line_coverage'}}}
+          {{{componentViewerHeaderLink lines_to_cover 'lines_to_cover' 'js-filter-lines-to-cover'}}}
+          {{{componentViewerHeaderLink uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines'}}}
+        </ul>
+      </div>
+    {{/any}}
 
+    {{#any branch_coverage conditions_to_cover covered_conditions uncovered_conditions}}
+      <div class="component-viewer-header-expanded-bar-section">
+        <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
+        <ul class="component-viewer-header-expanded-bar-section-list">
+          <li><span class="item">&nbsp;</span></li>
+          {{{componentViewerHeaderItem branch_coverage 'branch_coverage'}}}
+          {{{componentViewerHeaderLink conditions_to_cover 'conditions_to_cover' 'js-filter-branches-to-cover'}}}
+          {{{componentViewerHeaderLink uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches'}}}
+        </ul>
+      </div>
+    {{/any}}
+  </span>
 
-  {{#any it_coverage it_line_coverage it_lines_to_cover it_covered_lines it_uncovered_lines}}
-    <div class="component-viewer-header-expanded-bar-section">
-      <div class="component-viewer-header-expanded-bar-section-title">
-        {{t 'component_viewer.measure_section.integration_tests'}}
+  <span class="nowrap">
+    {{#any it_coverage it_line_coverage it_lines_to_cover it_covered_lines it_uncovered_lines}}
+      <div class="component-viewer-header-expanded-bar-section">
+        <div class="component-viewer-header-expanded-bar-section-title">
+          {{t 'component_viewer.measure_section.integration_tests'}}
+        </div>
+        <ul class="component-viewer-header-expanded-bar-section-list">
+          {{{componentViewerHeaderItem it_coverage 'coverage'}}}
+          {{{componentViewerHeaderItem it_line_coverage 'line_coverage'}}}
+          {{{componentViewerHeaderLink it_lines_to_cover 'lines_to_cover' 'js-filter-lines-to-cover-it'}}}
+          {{{componentViewerHeaderLink it_uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines-it'}}}
+        </ul>
       </div>
-      <ul class="component-viewer-header-expanded-bar-section-list">
-        {{{componentViewerHeaderItem it_coverage 'coverage'}}}
-        {{{componentViewerHeaderItem it_line_coverage 'line_coverage'}}}
-        {{{componentViewerHeaderLink it_lines_to_cover 'lines_to_cover' 'js-filter-lines-to-cover-it'}}}
-        {{{componentViewerHeaderLink it_uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines-it'}}}
-      </ul>
-    </div>
-  {{/any}}
+    {{/any}}
 
-  {{#any it_branch_coverage it_conditions_to_cover it_covered_conditions it_uncovered_conditions}}
-    <div class="component-viewer-header-expanded-bar-section">
-      <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
-      <ul class="component-viewer-header-expanded-bar-section-list">
-        <li><span class="item">&nbsp;</span></li>
-        {{{componentViewerHeaderItem it_branch_coverage 'branch_coverage'}}}
-        {{{componentViewerHeaderLink it_conditions_to_cover 'conditions_to_cover' 'js-filter-branches-to-cover-it'}}}
-        {{{componentViewerHeaderLink it_uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches-it'}}}
-      </ul>
-    </div>
-  {{/any}}
-{{/with}}
\ No newline at end of file
+    {{#any it_branch_coverage it_conditions_to_cover it_covered_conditions it_uncovered_conditions}}
+      <div class="component-viewer-header-expanded-bar-section">
+        <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
+        <ul class="component-viewer-header-expanded-bar-section-list">
+          <li><span class="item">&nbsp;</span></li>
+          {{{componentViewerHeaderItem it_branch_coverage 'branch_coverage'}}}
+          {{{componentViewerHeaderLink it_conditions_to_cover 'conditions_to_cover' 'js-filter-branches-to-cover-it'}}}
+          {{{componentViewerHeaderLink it_uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches-it'}}}
+        </ul>
+      </div>
+    {{/any}}
+  </span>
+{{/with}}
index c463283044005d7769aca7c5406a66cb990164eb..bca87c9c4a059bd7d18c75ac3e9a38cbdf9c74b5 100644 (file)
@@ -1,3 +1,9 @@
+<div class="component-viewer-header-time-changes">
+  <a class="highlighted-link js-issues-time-changes">
+    {{#if period}}Added {{period.label}}{{else}}<i class="icon-period"></i> Time Changes{{/if}}
+  </a>
+</div>
+
 {{#ifNotEmpty state.severities}}
   <div class="component-viewer-header-expanded-bar-section">
     <div class="component-viewer-header-expanded-bar-section-title">
   </ul>
 </div>
 
-  <div class="component-viewer-header-expanded-bar-section component-viewer-header-expanded-bar-section-actions">
-    <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      <li><a class="link-action js-issues-time-changes">
-        <span><i class="icon-period"></i>
-          {{#if period}}Added {{period.label}}{{else}}Time Changes{{/if}}</span>
-
+<div class="component-viewer-header-expanded-bar-section component-viewer-header-expanded-bar-section-actions">
+  <div class="component-viewer-header-expanded-bar-section-title">&nbsp;</div>
+  <ul class="component-viewer-header-expanded-bar-section-list">
+    {{#if state.canBulkChange}}
+      <li><a class="link-action js-issues-bulk-change">
+        <span><i class="icon-bulk-change"></i> {{t 'bulk_change'}}</span>
       </a></li>
-      {{#if state.canBulkChange}}
-        <li><a class="link-action js-issues-bulk-change">
-          <span><i class="icon-bulk-change"></i> {{t 'bulk_change'}}</span>
-        </a></li>
-      {{/if}}
-    </ul>
-  </div>
+    {{/if}}
+  </ul>
+</div>
diff --git a/sonar-server/src/main/hbs/component-viewer/header/scm-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/scm-header.hbs
new file mode 100644 (file)
index 0000000..8f60976
--- /dev/null
@@ -0,0 +1,5 @@
+<div class="component-viewer-header-time-changes">
+  <a class="highlighted-link js-scm-time-changes">
+    {{#if period}}Δ {{period.label}}{{else}}<i class="icon-period"></i> Time Changes{{/if}}
+  </a>
+</div>
\ No newline at end of file
index 7f290b8cbc2ec2bb703c878416a1c86dae2d5e87..b4f9c67709f891b40cefc486b4dc5cb308697e22 100644 (file)
         {{#if ../../settings.scm}}
           <td class="stat {{#if scm}}scm{{/if}}">
             {{#if scm}}
-              <span class="scm-date">{{scm.date}}</span>
-              <span class="scm-author" title="{{scm.author}}">{{scm.author}}</span>
+              {{#ifSCMChanged ../../../../source ../../../lineNumber}}
+                <span class="scm-date">{{scm.date}}</span>
+                <span class="scm-author" title="{{scm.author}}">{{scm.author}}</span>
+              {{/ifSCMChanged}}
             {{/if}}
           </td>
         {{/if}}
index 748bd4d914564b12a6f8a629ceaf8276bcebe9c6..615164e48cf2c92c449c759789ef099a7a8c3e4f 100644 (file)
@@ -2,7 +2,7 @@
 
 <ul class="component-viewer-popup-list">
   {{#each periods}}
-    <li><a class="link-action" data-period="{{key}}">{{label}}</a></li>
+    <li><a class="link-action" data-period="{{key}}">{{#if key}}{{../../prefix}}{{/if}} {{label}}</a></li>
   {{/each}}
 </ul>
 
index 8d7ada4f5269e2ecda74254fa13ee82e7f0da88c..bc43649e1db21cb3556abbeefcfbe9314ee8cb10 100644 (file)
@@ -315,4 +315,16 @@ define(['handlebars', 'moment'], function (Handlebars, moment) {
     }
   });
 
+  Handlebars.registerHelper('ifSCMChanged', function(source, line, options) {
+    var currentLine = _.findWhere(source, { lineNumber: line }),
+        prevLine = _.findWhere(source, { lineNumber: line - 1 }),
+        changed = true;
+    if (currentLine && prevLine && currentLine.scm && prevLine.scm) {
+      changed = (currentLine.scm.author !== prevLine.scm.author)
+          || (currentLine.scm.date !== prevLine.scm.date)
+          || (!prevLine.show);
+    }
+    return changed ? options.fn(this) : options.inverse(this);
+  });
+
 });
index eba86383eee9986a1c459ab6c1f1ac0205344cd5..2d5883bf4447a5b1a7e58e37869b772d14f17d79 100644 (file)
 
     var apiUrl = baseUrl + '/api/l10n/index';
     return jQuery.ajax({
-      'url': apiUrl,
-      'data': params,
-      'statusCode': {
+      url: apiUrl,
+      data: params,
+      dataType: 'json',
+      statusCode: {
         304: function() {
           // NOP, use cached messages
         }
index 98143dca2407d43daec24880a1cbe923ccc29c8d..e5e1bc33ca47891fc9b60f21a6c93d4ba8735788 100644 (file)
   &:hover { background-color: @barBackgroundColor; }
 }
 
-
 .component-viewer-header-decoration {
   margin-top: 10px;
 }
 
+.component-viewer-header-time-changes {
+  padding: 10px;
+}
+
 
 
 @popupArrowSize: 8px;