]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5209 Big refactoring
authorStas Vilchik <vilchiks@gmail.com>
Fri, 6 Jun 2014 07:02:48 +0000 (13:02 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Fri, 6 Jun 2014 07:02:57 +0000 (13:02 +0600)
35 files changed:
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-server/src/main/coffee/component-viewer/coverage-popup.coffee
sonar-server/src/main/coffee/component-viewer/header.coffee
sonar-server/src/main/coffee/component-viewer/header/base-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/header/basic-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/header/coverage-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/header/duplications-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/header/issues-header.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/header/tests-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 [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/mixins/main-duplications.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/mixins/main-issues.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/mixins/main-scm.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/models/component.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/models/period.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/models/state.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/component-viewer/source.coffee
sonar-server/src/main/coffee/component-viewer/time-changes-popup.coffee
sonar-server/src/main/coffee/component-viewer/utils.coffee
sonar-server/src/main/hbs/component-viewer/header.hbs
sonar-server/src/main/hbs/component-viewer/header/_basic-header.hbs [deleted file]
sonar-server/src/main/hbs/component-viewer/header/_coverage-header.hbs [deleted file]
sonar-server/src/main/hbs/component-viewer/header/_duplications-header.hbs [deleted file]
sonar-server/src/main/hbs/component-viewer/header/_issues-header.hbs [deleted file]
sonar-server/src/main/hbs/component-viewer/header/_tests-header.hbs [deleted file]
sonar-server/src/main/hbs/component-viewer/header/basic-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/header/coverage-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/header/duplications-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/header/issues-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/header/tests-header.hbs [new file with mode: 0644]
sonar-server/src/main/hbs/component-viewer/time-changes-popup.hbs
sonar-server/src/main/less/component-viewer.less
sonar-server/src/main/less/icons.less
sonar-server/src/main/less/mixins.less

index 157bc670a716018fff5039f909147abdeec65c26..17cd67057f83e264502c57dac71ebcddbea70d26 100644 (file)
@@ -2673,4 +2673,4 @@ component_viewer.transition.coverage=Covered By
 component_viewer.transition.covers=Covers
 component_viewer.transition.duplication=Duplicated By
 
-component_viewer.x_lines_are_covered={0} lines are covered
+component_viewer.x_lines_are_covered={0} lines are covered
\ No newline at end of file
index feffcd6905415c84e959e93d5ba712d6c123e0eb..5ab7aa689a705ae4318e0ea37b883c3d232a15c9 100644 (file)
@@ -32,9 +32,10 @@ define [
         name: x.name
         subname: x.dir
         active: file.key == key
-      if method?
-        @options.main.component.set 'selectedTest', method
-      @options.main._open key
+      @options.main._open(key).done =>
+        @options.main.headerView.enableBar('tests').done =>
+          if method?
+            @options.main.headerView.enableUnitTest method
 
 
     serializeData: ->
index 2bbf23899410d37dae13b389e00fe5f0b8404178..bbe11055e9a6f55dcd2a0813dd4fc56a26a23056 100644 (file)
@@ -1,27 +1,53 @@
 define [
   'backbone.marionette'
   'templates/component-viewer'
-  'component-viewer/covered-files-popup'
+
+  'component-viewer/header/basic-header'
+  'component-viewer/header/issues-header'
+  'component-viewer/header/coverage-header'
+  'component-viewer/header/duplications-header'
+  'component-viewer/header/tests-header'
+
+  'component-viewer/time-changes-popup'
+
   'common/handlebars-extensions'
 ], (
   Marionette
   Templates
-  CoveredFilesPopupView
+
+  BasicHeaderView
+  IssuesHeaderView
+  CoverageHeaderView
+  DuplicationsHeaderView
+  TestsHeaderView
+
+  TimeChangesPopupView
 ) ->
 
   $ = jQuery
 
   API_FAVORITE = "#{baseUrl}/api/favourites"
-  API_TESTS_COVERED_FILES = "#{baseUrl}/api/tests/covered_files"
+  BARS = [
+    { scope: 'basic', view: BasicHeaderView }
+    { scope: 'issues', view: IssuesHeaderView }
+    { scope: 'coverage', view: CoverageHeaderView }
+    { scope: 'duplications', view: DuplicationsHeaderView }
+    { scope: 'tests', view: TestsHeaderView }
+  ]
 
 
-  class HeaderView extends Marionette.Layout
+  class extends Marionette.Layout
     template: Templates['header']
 
 
+    regions:
+      barRegion: '.component-viewer-header-expanded-bar'
+
+
     ui:
       expandLinks: '.component-viewer-header-measures-expand'
-      expandedBars: '.component-viewer-header-expanded-bar'
+      expandedBar: '.component-viewer-header-expanded-bar'
+      spinnerBar: '.component-viewer-header-expanded-bar[data-scope=spinner]'
       unitTests: '.js-unit-test'
 
 
@@ -35,50 +61,22 @@ define [
       'click .js-toggle-duplications': 'toggleDuplications'
       'click .js-toggle-scm': 'toggleSCM'
 
-      'click .js-issues-bulk-change': 'issuesBulkChange'
-
-      'click .js-filter-current-issue': 'filterByCurrentIssue'
-      'click .js-filter-all-issues': 'filterByAllIssues'
-      'click .js-filter-rule': 'filterByRule'
-      'click .js-filter-fixed-issues': 'filterByFixedIssues'
-      'click .js-filter-unresolved-issues': 'filterByUnresolvedIssues'
-      'click .js-filter-false-positive-issues': 'filterByFalsePositiveIssues'
-      'click .js-filter-BLOCKER-issues': 'filterByBlockerIssues'
-      'click .js-filter-CRITICAL-issues': 'filterByCriticalIssues'
-      'click .js-filter-MAJOR-issues': 'filterByMajorIssues'
-      'click .js-filter-MINOR-issues': 'filterByMinorIssues'
-      'click .js-filter-INFO-issues': 'filterByInfoIssues'
-
-      'click .js-filter-lines-to-cover': 'filterByLinesToCover'
-      'click .js-filter-covered-lines': 'filterByCoveredLines'
-      'click .js-filter-uncovered-lines': 'filterByUncoveredLines'
-      'click .js-filter-branches-to-cover': 'filterByBranchesToCover'
-      'click .js-filter-covered-branches': 'filterByCoveredBranches'
-      'click .js-filter-uncovered-branches': 'filterByUncoveredBranches'
-      'click .js-filter-lines-to-cover-it': 'filterByLinesToCoverIT'
-      'click .js-filter-covered-lines-it': 'filterByCoveredLinesIT'
-      'click .js-filter-uncovered-lines-it': 'filterByUncoveredLinesIT'
-      'click .js-filter-branches-to-cover-it': 'filterByBranchesToCoverIT'
-      'click .js-filter-covered-branches-it': 'filterByCoveredBranchesIT'
-      'click .js-filter-uncovered-branches-it': 'filterByUncoveredBranchesIT'
-
-      'click .js-filter-duplications': 'filterByDuplications'
-
-      'click @ui.unitTests': 'showCoveredFiles'
+      'click .component-viewer-header-time-changes a': 'showTimeChangesPopup'
 
 
     initialize: (options) ->
       options.main.settings.on 'change', => @changeSettings()
+      @state = options.main.state
+      @component = options.main.component
+      @settings = options.main.component
 
 
     onRender: ->
       @delegateEvents()
-      if @options.main.component.get('q') == 'UTS'
-        @ui.expandLinks.filter("[data-scope=tests]").click()
 
 
     toggleFavorite: ->
-      component = @options.main.component
+      component = @component
       if component.get 'fav'
         $.ajax
           url: "#{API_FAVORITE}/#{component.get 'key'}"
@@ -96,30 +94,44 @@ define [
           @render()
 
 
+    showBarSpinner: ->
+      @ui.spinnerBar.addClass 'active'
+
+
+    hideBarSpinner: ->
+      @ui.spinnerBar.removeClass 'active'
+
+
+    resetBars: ->
+      @state.set 'activeHeaderTab', null
+      @ui.expandLinks.removeClass 'active'
+      @ui.expandedBar.removeClass 'active'
+      @barRegion.reset()
+
+
+    enableBar: (scope) ->
+      @ui.expandedBar.addClass 'active'
+      requests = []
+      unless @state.get 'hasMeasures'
+        requests.push @options.main.requestMeasures @options.main.key
+      if @component.get('isUnitTest') && !@state.get('hasTests')
+        requests.push @options.main.requestTests @options.main.key
+      $.when.apply($, requests).done =>
+        @state.set 'activeHeaderTab', scope
+        bar = _.findWhere BARS, scope: scope
+        @barRegion.show new bar.view
+          state: @state, component: @component, settings: @settings, source: @model, header: @
+        @ui.expandLinks.filter("[data-scope=#{scope}]").addClass 'active'
+
 
     showExpandedBar: (e) ->
       el = $(e.currentTarget)
       active = el.is '.active'
-      @ui.expandLinks.removeClass 'active'
-      @ui.expandedBars.hide()
+      @resetBars()
       unless active
         el.addClass 'active'
         scope = el.data 'scope'
-        unless @options.main.component.has 'msr'
-          req = @options.main.requestMeasures(@options.main.key)
-          if @options.main.component.get('q') == 'UTS'
-            req = $.when req, @options.main.requestTests(@options.main.key)
-          req.done =>
-            @render()
-            @ui.expandLinks.filter("[data-scope=#{scope}]").addClass 'active'
-            @ui.expandedBars.filter("[data-scope=#{scope}]").show()
-
-            method = @options.main.component.get 'selectedTest'
-            if method?
-              @options.main.component.unset 'selectedTest'
-              @ui.unitTests.filter("[data-name=#{method}]").click().addClass('active')
-        else
-          @ui.expandedBars.filter("[data-scope=#{scope}]").show()
+        @enableBar scope
 
 
     changeSettings: ->
@@ -145,11 +157,13 @@ define [
     toggleWorkspace: (e) -> @toggleSetting e, @options.main.showWorkspace, @options.main.hideWorkspace
 
 
-    issuesBulkChange: ->
-      issues = @model.get('activeIssues')?.map (issue) -> issue.key
-      if issues.length > 0
-        url = "#{baseUrl}/issues/bulk_change_form?issues=#{issues.join()}"
-        openModalWindow url, {}
+    showTimeChangesPopup: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      popup = new TimeChangesPopupView
+        triggerEl: $(e.currentTarget)
+        main: @options.main
+      popup.render()
 
 
     filterLines: (e, methodName, extra) ->
@@ -159,60 +173,8 @@ define [
       method.call @options.main, extra
 
 
-    # Issues
-    filterByCurrentIssue: (e) -> @filterLines e, 'filterByCurrentIssue'
-    filterByAllIssues: (e) -> @filterLines e, 'filterByAllIssues'
-    filterByFixedIssues: (e) -> @filterLines e, 'filterByFixedIssues'
-    filterByUnresolvedIssues: (e) -> @filterLines e, 'filterByUnresolvedIssues'
-    filterByFalsePositiveIssues: (e) -> @filterLines e, 'filterByFalsePositiveIssues'
-
-    filterByRule: (e) -> @filterLines e, 'filterByRule', $(e.currentTarget).data 'rule'
-
-    filterByBlockerIssues: (e) -> @filterLines e, 'filterByBlockerIssues'
-    filterByCriticalIssues: (e) -> @filterLines e, 'filterByCriticalIssues'
-    filterByMajorIssues: (e) -> @filterLines e, 'filterByMajorIssues'
-    filterByMinorIssues: (e) -> @filterLines e, 'filterByMinorIssues'
-    filterByInfoIssues: (e) -> @filterLines e, 'filterByInfoIssues'
-
-
-    # Coverage
-    filterByLinesToCover: (e) -> @filterLines e, 'filterByLinesToCover'
-    filterByCoveredLines: (e) -> @filterLines e, 'filterByCoveredLines'
-    filterByUncoveredLines: (e) -> @filterLines e, 'filterByUncoveredLines'
-    filterByBranchesToCover: (e) -> @filterLines e, 'filterByBranchesToCover'
-    filterByCoveredBranches: (e) -> @filterLines e, 'filterByCoveredBranches'
-    filterByUncoveredBranches: (e) -> @filterLines e, 'filterByUncoveredBranches'
-
-    filterByLinesToCoverIT: (e) -> @filterLines e, 'filterByLinesToCoverIT'
-    filterByCoveredLinesIT: (e) -> @filterLines e, 'filterByCoveredLinesIT'
-    filterByUncoveredLinesIT: (e) -> @filterLines e, 'filterByUncoveredLinesIT'
-    filterByBranchesToCoverIT: (e) -> @filterLines e, 'filterByBranchesToCoverIT'
-    filterByCoveredBranchesIT: (e) -> @filterLines e, 'filterByCoveredBranchesIT'
-    filterByUncoveredBranchesIT: (e) -> @filterLines e, 'filterByUncoveredBranchesIT'
-
-
-    # Duplications
-    filterByDuplications: (e) -> @filterLines e, 'filterByDuplications'
-
-
-    showCoveredFiles: (e) ->
-      e.stopPropagation()
-      $('body').click()
-      @$('.component-viewer-header-expanded-bar-section-list .active').removeClass 'active'
-      testName = $(e.currentTarget).data 'name'
-      test = _.findWhere @options.main.component.get('tests'), name: testName
-      key = @options.main.component.get('key')
-      $.get API_TESTS_COVERED_FILES, key: key, test: testName, (data) =>
-        popup = new CoveredFilesPopupView
-          triggerEl: $(e.currentTarget)
-          collection: new Backbone.Collection data.files
-          test: test
-          main: @options.main
-        popup.render()
-
-
     serializeData: ->
-      component = @options.main.component.toJSON()
+      component = @component.toJSON()
       if component.measures
         component.measures.maxIssues = Math.max(
           component.measures.fBlockerIssues || 0
@@ -222,11 +184,9 @@ define [
           component.measures.fInfoIssues || 0
         )
 
-      if component.severities
-        order = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
-        component.severities = _.sortBy component.severities, (s) -> order.indexOf s[0]
-
       settings: @options.main.settings.toJSON()
+      state: @state.toJSON()
       showSettings: @showSettings
       component: component
-      currentIssue: @options.main.currentIssue
\ No newline at end of file
+      currentIssue: @options.main.currentIssue
+      period: @options.main.period?.toJSON()
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/header/base-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/base-header.coffee
new file mode 100644 (file)
index 0000000..2d870bf
--- /dev/null
@@ -0,0 +1,22 @@
+define [
+  'backbone.marionette'
+], (
+  Marionette
+) ->
+
+  class extends Marionette.ItemView
+
+    initialize: (options) ->
+      super
+      @state = options.state
+      @component = options.component
+      @settings = options.settings
+      @source = options.source
+      @header = options.header
+
+
+    serializeData: ->
+      _.extend super,
+        state: @state.toJSON()
+        component: @component.toJSON()
+        settings: @settings.toJSON()
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/header/basic-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/basic-header.coffee
new file mode 100644 (file)
index 0000000..74ce85f
--- /dev/null
@@ -0,0 +1,13 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+) ->
+
+
+  class extends BaseHeaderView
+    template: Templates['basic-header']
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/header/coverage-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/coverage-header.coffee
new file mode 100644 (file)
index 0000000..39f8f8e
--- /dev/null
@@ -0,0 +1,39 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+) ->
+
+
+  class extends BaseHeaderView
+    template: Templates['coverage-header']
+
+
+    events:
+      'click .js-filter-lines-to-cover': 'filterByLinesToCover'
+      'click .js-filter-uncovered-lines': 'filterByUncoveredLines'
+      'click .js-filter-branches-to-cover': 'filterByBranchesToCover'
+      'click .js-filter-uncovered-branches': 'filterByUncoveredBranches'
+      'click .js-filter-lines-to-cover-it': 'filterByLinesToCoverIT'
+      'click .js-filter-uncovered-lines-it': 'filterByUncoveredLinesIT'
+      'click .js-filter-branches-to-cover-it': 'filterByBranchesToCoverIT'
+      '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'
+
+    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
diff --git a/sonar-server/src/main/coffee/component-viewer/header/duplications-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/duplications-header.coffee
new file mode 100644 (file)
index 0000000..94d7205
--- /dev/null
@@ -0,0 +1,20 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+) ->
+
+
+  class extends BaseHeaderView
+    template: Templates['duplications-header']
+
+
+    events:
+      'click .js-filter-duplications': 'filterByDuplications'
+
+
+    filterByDuplications: (e) -> @header.filterLines e, 'filterByDuplications'
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/header/issues-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/issues-header.coffee
new file mode 100644 (file)
index 0000000..5699961
--- /dev/null
@@ -0,0 +1,53 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+) ->
+
+  $ = jQuery
+
+
+  class extends BaseHeaderView
+    template: Templates['issues-header']
+
+
+    events:
+      'click .js-issues-bulk-change': 'issuesBulkChange'
+
+      'click .js-filter-current-issue': 'filterByCurrentIssue'
+      'click .js-filter-all-issues': 'filterByAllIssues'
+      'click .js-filter-rule': 'filterByRule'
+      'click .js-filter-fixed-issues': 'filterByFixedIssues'
+      'click .js-filter-unresolved-issues': 'filterByUnresolvedIssues'
+      'click .js-filter-false-positive-issues': 'filterByFalsePositiveIssues'
+      'click .js-filter-BLOCKER-issues': 'filterByBlockerIssues'
+      'click .js-filter-CRITICAL-issues': 'filterByCriticalIssues'
+      'click .js-filter-MAJOR-issues': 'filterByMajorIssues'
+      'click .js-filter-MINOR-issues': 'filterByMinorIssues'
+      'click .js-filter-INFO-issues': 'filterByInfoIssues'
+
+
+    issuesBulkChange: ->
+      issues = @source.get('activeIssues')?.map (issue) -> issue.key
+      if issues.length > 0
+        url = "#{baseUrl}/issues/bulk_change_form?issues=#{issues.join()}"
+        openModalWindow url, {}
+
+
+    filterByCurrentIssue: (e) -> @header.filterLines e, 'filterByCurrentIssue'
+    filterByAllIssues: (e) -> @header.filterLines e, 'filterByAllIssues'
+    filterByFixedIssues: (e) -> @header.filterLines e, 'filterByFixedIssues'
+    filterByUnresolvedIssues: (e) -> @header.filterLines e, 'filterByUnresolvedIssues'
+    filterByFalsePositiveIssues: (e) -> @header.filterLines e, 'filterByFalsePositiveIssues'
+
+    filterByRule: (e) -> @header.filterLines e, 'filterByRule', $(e.currentTarget).data 'rule'
+
+    filterByBlockerIssues: (e) -> @header.filterLines e, 'filterByBlockerIssues'
+    filterByCriticalIssues: (e) -> @header.filterLines e, 'filterByCriticalIssues'
+    filterByMajorIssues: (e) -> @header.filterLines e, 'filterByMajorIssues'
+    filterByMinorIssues: (e) -> @header.filterLines e, 'filterByMinorIssues'
+    filterByInfoIssues: (e) -> @header.filterLines e, 'filterByInfoIssues'
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/header/tests-header.coffee b/sonar-server/src/main/coffee/component-viewer/header/tests-header.coffee
new file mode 100644 (file)
index 0000000..ddcebd2
--- /dev/null
@@ -0,0 +1,50 @@
+define [
+  'backbone.marionette'
+  'templates/component-viewer'
+  'component-viewer/header/base-header'
+  'component-viewer/covered-files-popup'
+], (
+  Marionette
+  Templates
+  BaseHeaderView
+  CoveredFilesPopupView
+) ->
+
+  $ = jQuery
+  API_TESTS_COVERED_FILES = "#{baseUrl}/api/tests/covered_files"
+
+
+  class extends BaseHeaderView
+    template: Templates['tests-header']
+
+
+    ui:
+      unitTests: '.js-unit-test'
+
+
+    events:
+      'click @ui.unitTests': 'showCoveredFiles'
+
+
+    onRender: ->
+      @header.enableUnitTest = (testName) =>
+        @ui.unitTests.filter("[data-name=#{testName}]").click()
+
+
+    onClose: ->
+      delete @header.enableUnitTest
+
+
+    showCoveredFiles: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      testName = $(e.currentTarget).data 'name'
+      test = _.findWhere @component.get('tests'), name: testName
+      key = @component.get('key')
+      $.get API_TESTS_COVERED_FILES, key: key, test: testName, (data) =>
+        popup = new CoveredFilesPopupView
+          triggerEl: $(e.currentTarget)
+          collection: new Backbone.Collection data.files
+          test: test
+          main: @options.main
+        popup.render()
\ No newline at end of file
index de602df1de202802eba9485e27af051e729046ea..11f54131d602d401e3d9086d3083f30e3506ba65 100644 (file)
@@ -2,6 +2,16 @@ define [
   'backbone'
   'backbone.marionette'
   'templates/component-viewer'
+
+  'component-viewer/models/state'
+  'component-viewer/models/component'
+  'component-viewer/models/period'
+
+  'component-viewer/mixins/main-issues'
+  'component-viewer/mixins/main-coverage'
+  'component-viewer/mixins/main-duplications'
+  'component-viewer/mixins/main-scm'
+
   'component-viewer/workspace'
   'component-viewer/source'
   'component-viewer/header'
@@ -12,6 +22,16 @@ define [
   Backbone
   Marionette
   Templates
+
+  State
+  Component
+  Period
+
+  IssuesMixin
+  CoverageMixin
+  DuplicationsMixin
+  SCMMixin
+
   WorkspaceView
   SourceView
   HeaderView
@@ -22,17 +42,9 @@ define [
 
   API_COMPONENT = "#{baseUrl}/api/components/app"
   API_SOURCES = "#{baseUrl}/api/sources/show"
-  API_ISSUES = "#{baseUrl}/api/issues/search"
-  API_COVERAGE = "#{baseUrl}/api/coverage/show"
-  API_SCM = "#{baseUrl}/api/sources/scm"
   API_MEASURES = "#{baseUrl}/api/resources"
-  API_DUPLICATIONS = "#{baseUrl}/api/duplications/show"
   API_TESTS = "#{baseUrl}/api/tests/show"
 
-  LINES_AROUND_ISSUE = 4
-  LINES_AROUND_COVERED_LINE = 1
-  LINES_AROUND_DUPLICATION = 1
-
   SOURCE_METRIC_LIST = 'accessors,classes,functions,statements,' +
     'ncloc,lines,' +
     'complexity,function_complexity,' +
@@ -52,7 +64,7 @@ define [
 
 
 
-  class ComponentViewer extends Marionette.Layout
+  class ComponentViewer extends utils.mixOf Marionette.Layout, IssuesMixin, CoverageMixin, DuplicationsMixin, SCMMixin
     className: 'component-viewer'
     template: Templates['layout']
 
@@ -67,7 +79,9 @@ define [
       @settings = new Backbone.Model @getDefaultSettings()
       @settings.set options.settings
 
-      @component = new Backbone.Model()
+      @state = new State()
+
+      @component = new Component()
       @component.set options.component if options.component?
 
       @workspace = new Backbone.Collection()
@@ -84,7 +98,8 @@ define [
         model: @source
         main: @
 
-      @requestIssuesOnce = false
+      @period = null
+      @periods = new Backbone.Collection [], model: Period
 
 
     getDefaultSettings: ->
@@ -109,35 +124,44 @@ define [
 
 
     requestComponent: (key, clear = false) ->
+      STATE_FIELDS = ['canBulkChange', 'canMarkAsFavourite', 'scmAvailable']
+      COMPONENT_FIELDS = ['key', 'name', 'path', 'q', 'projectName', 'subProjectName', 'measures', 'fav']
+
       $.get API_COMPONENT, key: key, (data) =>
+        # Component
         @component.clear() if clear
-        @component.set data
+        COMPONENT_FIELDS.forEach (f) => @component.set f, data[f]
         @component.set 'dir', utils.splitLongName(data.path).dir
+        @component.set 'isUnitTest', data.q == 'UTS'
+
+        # State
+        stateAttributes = {}
+        STATE_FIELDS.forEach (f) -> stateAttributes[f] = data[f]
+        rules = data.rules.map (r) -> key: r[0], name: r[1], count: r[2]
+        stateAttributes.rules = _.sortBy rules, 'name'
+        severities = data.severities.map (r) -> key: r[0], name: r[1], count: r[2]
+        stateAttributes.severities = utils.sortSeverities severities
+        @state.clear silent: true
+        @state.set _.defaults stateAttributes, @state.defaults
+
+        # Periods
+        @periods.reset [{}]
+        data.periods.forEach (p) => @periods.add key: p[0], label: p[1], sinceDate: new Date p[2]
+        @period = @periods.at 0
 
 
     requestMeasures: (key) ->
-      unless @component.get('q') == 'UTS'
+      @state.set 'hasMeasures', true
+      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]
       $.get API_MEASURES, resource: key, metrics: metrics, (data) =>
         measuresList = data[0].msr || []
-        measures = {}
-        measuresList.forEach (m) -> measures[m.key] = m.frmt_val
-
-        if measures['lines_to_cover']? && measures['uncovered_lines']?
-          measures['covered_lines'] = measures['lines_to_cover'] - measures['uncovered_lines']
-
-        if measures['conditions_to_cover']? && measures['uncovered_conditions']?
-          measures['covered_conditions'] = measures['conditions_to_cover'] - measures['uncovered_conditions']
-
-        if measures['it_lines_to_cover']? && measures['it_uncovered_lines']?
-          measures['it_covered_lines'] = measures['it_lines_to_cover'] - measures['it_uncovered_lines']
-
-        if measures['it_conditions_to_cover']? && measures['it_uncovered_conditions']?
-          measures['it_covered_conditions'] = measures['it_conditions_to_cover'] - measures['it_uncovered_conditions']
-
-        @component.set 'msr', measures
+        measures = @component.get 'measures'
+        measuresList.forEach (m) ->
+          measures[m.key] = m.frmt_val
+        @component.set 'measures', measures
 
 
     requestSource: (key) ->
@@ -146,34 +170,9 @@ define [
         @source.set source: data.sources
 
 
-    requestSCM: (key) ->
-      $.get API_SCM, key: key, (data) =>
-        @source.set scm: data.scm
-
-
-    requestIssues: (key) ->
-      options =
-        components: key
-        ps: 10000
-        extra_fields: 'actions,transitions,assigneeName,actionPlanName'
-      $.get API_ISSUES, options, (data) =>
-        @requestIssuesOnce = true
-        @source.set issues: data.issues
-
-
-    requestCoverage: (key, type = 'UT') ->
-      $.get API_COVERAGE, key: key, type: type, (data) =>
-        @source.set coverage: data.coverage
-
-
-    requestDuplications: (key) ->
-      $.get API_DUPLICATIONS, key: key, (data) =>
-        @source.set duplications: data.duplications
-        @source.set duplicationFiles: data.files
-
-
     requestTests: (key) ->
       $.get API_TESTS, key: key, (data) =>
+        @state.set 'hasTests', true
         @component.set 'tests', data.tests
 
 
@@ -199,21 +198,6 @@ define [
         if @settings.get('scm') then @showSCM() else @hideSCM()
 
 
-    showCoverage: (store = false) ->
-      @settings.set 'coverage', true
-      @storeSettings() if store
-      unless @source.has 'coverage'
-        @requestCoverage(@key).done => @sourceView.render()
-      else
-        @sourceView.render()
-
-
-    hideCoverage: (store = false) ->
-      @settings.set 'coverage', false
-      @storeSettings() if store
-      @sourceView.render()
-
-
     toggleWorkspace: (store = false) ->
       if @settings.get 'workspace' then @hideWorkspace() else @showWorkspace()
       @storeSettings() if store
@@ -231,52 +215,9 @@ define [
       @render()
 
 
-    showIssues: (store = false, issue) ->
-      @settings.set 'issues', true
-      @storeSettings() if store
-      if issue?
-        @currentIssue = issue.key
-        @source.set 'issues', [issue]
-        @filterByCurrentIssue()
-        @headerView.render()
-      else
-        @sourceView.render()
-
-
-    hideIssues: (store = false) ->
-      @settings.set 'issues', false
-      @storeSettings() if store
-      @sourceView.render()
-
-
-    showDuplications: (store = false) ->
-      @settings.set 'duplications', true
-      @storeSettings() if store
-      unless @source.has 'duplications'
-        @requestDuplications(@key).done => @sourceView.render()
-      else
-        @sourceView.render()
-
-
-    hideDuplications: (store = false) ->
-      @settings.set 'duplications', false
-      @storeSettings() if store
-      @sourceView.render()
-
-
-    showSCM: (store = false) ->
-      @settings.set 'scm', true
-      @storeSettings() if store
-      unless @source.has 'scm'
-        @requestSCM(@key).done => @sourceView.render()
-      else
-        @sourceView.render()
-
-
-    hideSCM: (store = false) ->
-      @settings.set 'scm', false
-      @storeSettings() if store
-      @sourceView.render()
+    enablePeriod: (period) ->
+      @period = @periods.findWhere key: period
+      @render()
 
 
     showAllLines: ->
@@ -285,119 +226,6 @@ define [
       @sourceView.render()
 
 
-    filterLinesByIssues: ->
-      issues = @source.get 'issues'
-      @sourceView.resetShowBlocks()
-      issues.forEach (issue) =>
-        line = issue.line || 0
-        @sourceView.addShowBlock line - LINES_AROUND_ISSUE, line + LINES_AROUND_ISSUE
-      @sourceView.render()
-
-
-    filterByIssues: (predicate, requestIssues = true) ->
-      if requestIssues && !@requestIssuesOnce
-        @requestIssues(@key).done => @_filterByIssues(predicate)
-      else
-        @_filterByIssues(predicate)
-
-
-    _filterByIssues: (predicate) ->
-      issues = @source.get 'issues'
-      @settings.set 'issues', true
-      @sourceView.resetShowBlocks()
-      activeIssues = []
-      issues.forEach (issue) =>
-        if predicate issue
-          line = issue.line || 0
-          @sourceView.addShowBlock line - LINES_AROUND_ISSUE, line + LINES_AROUND_ISSUE
-          activeIssues.push issue
-      @source.set 'activeIssues', activeIssues
-      @sourceView.render()
-
-
-    # Current Issue
-    filterByCurrentIssue: -> @filterByIssues ((issue) => issue.key == @currentIssue), false
-
-    # All Issues
-    filterByAllIssues: -> @filterByIssues -> true
-
-    # Resolved Issues
-    filterByFixedIssues: -> @filterByIssues (issue) -> issue.resolution == 'FIXED'
-
-    # Unresolved Issues
-    filterByUnresolvedIssues: -> @filterByIssues (issue) -> !issue.resolution
-
-    # False Positive
-    filterByFalsePositiveIssues: -> @filterByIssues (issue) -> issue.resolution == 'FALSE-POSITIVE'
-
-    # Rule
-    filterByRule: (rule) -> @filterByIssues (issue) -> issue.rule == rule && !issue.resolution
-
-    # Severity
-    filterByBlockerIssues: -> @filterByIssues (issue) -> issue.severity == 'BLOCKER' && !issue.resolution
-    filterByCriticalIssues: -> @filterByIssues (issue) -> issue.severity == 'CRITICAL' && !issue.resolution
-    filterByMajorIssues: -> @filterByIssues (issue) -> issue.severity == 'MAJOR' && !issue.resolution
-    filterByMinorIssues: -> @filterByIssues (issue) -> issue.severity == 'MINOR' && !issue.resolution
-    filterByInfoIssues: -> @filterByIssues (issue) -> issue.severity == 'INFO' && !issue.resolution
-
-
-    filterByCoverage: (predicate) ->
-      @requestCoverage(@key).done => @_filterByCoverage(predicate)
-
-
-    filterByCoverageIT: (predicate) ->
-      @requestCoverage(@key, 'IT').done => @_filterByCoverage(predicate)
-
-
-    _filterByCoverage: (predicate) ->
-      coverage = @source.get 'coverage'
-      @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
-      @sourceView.render()
-
-
-    # Unit Tests
-    filterByLinesToCover: -> @filterByCoverage (c) -> c[1]?
-    filterByCoveredLines: -> @filterByCoverage (c) -> c[1]? && c[1]
-    filterByUncoveredLines: -> @filterByCoverage (c) -> c[1]? && !c[1]
-    filterByBranchesToCover: -> @filterByCoverage (c) -> c[3]?
-    filterByCoveredBranches: -> @filterByCoverage (c) -> c[3]? && c[4]? && (c[4] > 0)
-    filterByUncoveredBranches: -> @filterByCoverage (c) -> c[3]? && c[4]? && (c[3] > c[4])
-
-    # Integration Tests
-    filterByLinesToCoverIT: -> @filterByCoverageIT (c) -> c[1]?
-    filterByCoveredLinesIT: -> @filterByCoverageIT (c) -> c[1]? && c[1]
-    filterByUncoveredLinesIT: -> @filterByCoverageIT (c) -> c[1]? && !c[1]
-    filterByBranchesToCoverIT: -> @filterByCoverageIT (c) -> c[3]?
-    filterByCoveredBranchesIT: -> @filterByCoverageIT (c) -> c[3]? && c[4]? && (c[4] > 0)
-    filterByUncoveredBranchesIT: -> @filterByCoverageIT (c) -> c[3]? && c[4]? && (c[3] > c[4])
-
-
-    # Duplications
-    filterByDuplications: ->
-      unless @source.has 'duplications'
-        @requestDuplications(@key).done => @_filterByDuplications()
-      else
-        @_filterByDuplications()
-
-
-    _filterByDuplications: ->
-      duplications = @source.get 'duplications'
-      @settings.set 'duplications', true
-      @sourceView.resetShowBlocks()
-      duplications.forEach (d) =>
-        d.blocks.forEach (b) =>
-          if b._ref == '1'
-            lineFrom = b.from
-            lineTo = b.from + b.size
-            @sourceView.addShowBlock lineFrom - LINES_AROUND_DUPLICATION, lineTo + LINES_AROUND_DUPLICATION
-      @sourceView.render()
-
-
     addTransition: (transition, options) ->
       @workspace.add
         key: @component.get 'key'
diff --git a/sonar-server/src/main/coffee/component-viewer/mixins/main-coverage.coffee b/sonar-server/src/main/coffee/component-viewer/mixins/main-coverage.coffee
new file mode 100644 (file)
index 0000000..88b5bb3
--- /dev/null
@@ -0,0 +1,60 @@
+define [], () ->
+
+  $ = jQuery
+  API_COVERAGE = "#{baseUrl}/api/coverage/show"
+  LINES_AROUND_COVERED_LINE = 1
+
+
+  class CoverageMixin
+
+    requestCoverage: (key, type = 'UT') ->
+      $.get API_COVERAGE, key: key, type: type, (data) =>
+        @state.set 'hasCoverage', true
+        @source.set coverage: data.coverage
+
+
+    showCoverage: (store = false) ->
+      @settings.set 'coverage', true
+      @storeSettings() if store
+      unless @state.get 'hasCoverage'
+        @requestCoverage(@key).done => @sourceView.render()
+      else
+        @sourceView.render()
+
+
+    hideCoverage: (store = false) ->
+      @settings.set 'coverage', false
+      @storeSettings() if store
+      @sourceView.render()
+
+
+    filterByCoverage: (predicate) ->
+      @requestCoverage(@key).done => @_filterByCoverage(predicate)
+
+
+    filterByCoverageIT: (predicate) ->
+      @requestCoverage(@key, 'IT').done => @_filterByCoverage(predicate)
+
+
+    _filterByCoverage: (predicate) ->
+      coverage = @source.get 'coverage'
+      @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
+      @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])
+
+    # 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
diff --git a/sonar-server/src/main/coffee/component-viewer/mixins/main-duplications.coffee b/sonar-server/src/main/coffee/component-viewer/mixins/main-duplications.coffee
new file mode 100644 (file)
index 0000000..6be9751
--- /dev/null
@@ -0,0 +1,50 @@
+define [], () ->
+
+  $ = jQuery
+  API_DUPLICATIONS = "#{baseUrl}/api/duplications/show"
+  LINES_AROUND_DUPLICATION = 1
+
+
+  class DuplicationsMixin
+
+    requestDuplications: (key) ->
+      $.get API_DUPLICATIONS, key: key, (data) =>
+        @state.set 'hasDuplications', true
+        @source.set duplications: data.duplications
+        @source.set duplicationFiles: data.files
+
+
+    showDuplications: (store = false) ->
+      @settings.set 'duplications', true
+      @storeSettings() if store
+      unless @state.get 'hasDuplications'
+        @requestDuplications(@key).done => @sourceView.render()
+      else
+        @sourceView.render()
+
+
+    hideDuplications: (store = false) ->
+      @settings.set 'duplications', false
+      @storeSettings() if store
+      @sourceView.render()
+
+
+    # Duplications
+    filterByDuplications: ->
+      unless @state.get 'hasDuplications'
+        @requestDuplications(@key).done => @_filterByDuplications()
+      else
+        @_filterByDuplications()
+
+
+    _filterByDuplications: ->
+      duplications = @source.get 'duplications'
+      @settings.set 'duplications', true
+      @sourceView.resetShowBlocks()
+      duplications.forEach (d) =>
+        d.blocks.forEach (b) =>
+          if b._ref == '1'
+            lineFrom = b.from
+            lineTo = b.from + b.size
+            @sourceView.addShowBlock lineFrom - LINES_AROUND_DUPLICATION, lineTo + LINES_AROUND_DUPLICATION
+      @sourceView.render()
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/mixins/main-issues.coffee b/sonar-server/src/main/coffee/component-viewer/mixins/main-issues.coffee
new file mode 100644 (file)
index 0000000..b840a27
--- /dev/null
@@ -0,0 +1,91 @@
+define [], () ->
+
+  $ = jQuery
+  API_ISSUES = "#{baseUrl}/api/issues/search"
+  LINES_AROUND_ISSUE = 4
+
+
+  class IssuesMixin
+
+    requestIssues: (key) ->
+      options =
+        components: key
+        ps: 10000
+        extra_fields: 'actions,transitions,assigneeName,actionPlanName'
+      $.get API_ISSUES, options, (data) =>
+        @state.set 'hasIssues', true
+        @source.set issues: data.issues
+
+
+    showIssues: (store = false, issue) ->
+      @settings.set 'issues', true
+      @storeSettings() if store
+      if issue?
+        @currentIssue = issue.key
+        @source.set 'issues', [issue]
+        @filterByCurrentIssue()
+        @headerView.render()
+      else
+        @sourceView.render()
+
+
+    hideIssues: (store = false) ->
+      @settings.set 'issues', false
+      @storeSettings() if store
+      @sourceView.render()
+
+
+    filterLinesByIssues: ->
+      issues = @source.get 'issues'
+      @sourceView.resetShowBlocks()
+      issues.forEach (issue) =>
+        line = issue.line || 0
+        @sourceView.addShowBlock line - LINES_AROUND_ISSUE, line + LINES_AROUND_ISSUE
+      @sourceView.render()
+
+
+    filterByIssues: (predicate, requestIssues = true) ->
+      if requestIssues && !@state.get 'hasIssues'
+        @requestIssues(@key).done => @_filterByIssues(predicate)
+      else
+        @_filterByIssues(predicate)
+
+
+    _filterByIssues: (predicate) ->
+      issues = @source.get 'issues'
+      @settings.set 'issues', true
+      @sourceView.resetShowBlocks()
+      activeIssues = []
+      issues.forEach (issue) =>
+        if predicate issue
+          line = issue.line || 0
+          @sourceView.addShowBlock line - LINES_AROUND_ISSUE, line + LINES_AROUND_ISSUE
+          activeIssues.push issue
+      @source.set 'activeIssues', activeIssues
+      @sourceView.render()
+
+
+    # Current Issue
+    filterByCurrentIssue: -> @filterByIssues ((issue) => issue.key == @currentIssue), false
+
+    # All Issues
+    filterByAllIssues: -> @filterByIssues -> true
+
+    # Resolved Issues
+    filterByFixedIssues: -> @filterByIssues (issue) -> issue.resolution == 'FIXED'
+
+    # Unresolved Issues
+    filterByUnresolvedIssues: -> @filterByIssues (issue) -> !issue.resolution
+
+    # False Positive
+    filterByFalsePositiveIssues: -> @filterByIssues (issue) -> issue.resolution == 'FALSE-POSITIVE'
+
+    # Rule
+    filterByRule: (rule) -> @filterByIssues (issue) -> issue.rule == rule && !issue.resolution
+
+    # Severity
+    filterByBlockerIssues: -> @filterByIssues (issue) -> issue.severity == 'BLOCKER' && !issue.resolution
+    filterByCriticalIssues: -> @filterByIssues (issue) -> issue.severity == 'CRITICAL' && !issue.resolution
+    filterByMajorIssues: -> @filterByIssues (issue) -> issue.severity == 'MAJOR' && !issue.resolution
+    filterByMinorIssues: -> @filterByIssues (issue) -> issue.severity == 'MINOR' && !issue.resolution
+    filterByInfoIssues: -> @filterByIssues (issue) -> issue.severity == 'INFO' && !issue.resolution
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/mixins/main-scm.coffee b/sonar-server/src/main/coffee/component-viewer/mixins/main-scm.coffee
new file mode 100644 (file)
index 0000000..3e92b75
--- /dev/null
@@ -0,0 +1,27 @@
+define [], () ->
+
+  $ = jQuery
+  API_SCM = "#{baseUrl}/api/sources/scm"
+
+
+  class SCMMixin
+
+    requestSCM: (key) ->
+      $.get API_SCM, key: key, (data) =>
+        @state.set 'hasSCM', true
+        @source.set scm: data.scm
+
+
+    showSCM: (store = false) ->
+      @settings.set 'scm', true
+      @storeSettings() if store
+      unless @state.get 'hasSCM'
+        @requestSCM(@key).done => @sourceView.render()
+      else
+        @sourceView.render()
+
+
+    hideSCM: (store = false) ->
+      @settings.set 'scm', false
+      @storeSettings() if store
+      @sourceView.render()
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/models/component.coffee b/sonar-server/src/main/coffee/component-viewer/models/component.coffee
new file mode 100644 (file)
index 0000000..414462f
--- /dev/null
@@ -0,0 +1,8 @@
+define [
+  'backbone'
+], (
+  Backbone
+) ->
+
+
+  class Component extends Backbone.Model
\ No newline at end of file
diff --git a/sonar-server/src/main/coffee/component-viewer/models/period.coffee b/sonar-server/src/main/coffee/component-viewer/models/period.coffee
new file mode 100644 (file)
index 0000000..e2faaa4
--- /dev/null
@@ -0,0 +1,13 @@
+define [
+  'backbone'
+], (
+  Backbone
+) ->
+
+
+  class Period extends Backbone.Model
+
+    defaults:
+      key: ''
+      label: t 'none'
+      sinceDate: null
diff --git a/sonar-server/src/main/coffee/component-viewer/models/state.coffee b/sonar-server/src/main/coffee/component-viewer/models/state.coffee
new file mode 100644 (file)
index 0000000..25b6a3a
--- /dev/null
@@ -0,0 +1,19 @@
+define [
+  'backbone'
+], (
+  Backbone
+) ->
+
+
+  class State extends Backbone.Model
+
+    defaults:
+      hasMeasures: false
+      hasIssues: false
+      hasCoverage: false
+      hasITCoverage: false
+      hasDuplications: false
+      hasTests: false
+      hasSCM: false
+
+      activeHeaderTab: null
\ No newline at end of file
index 49ea28125155b67cdb36aac703fafffb9646e1dd..0ace43ecefbaa4e8221f2e207dedff8bbef345c7 100644 (file)
@@ -174,16 +174,6 @@ define [
       @options.main.showAllLines()
 
 
-    toggleTimeChangePopup: (e) ->
-      e.stopPropagation()
-      $('body').click()
-      popup = new TimeChangesPopupView
-        triggerEl: $(e.currentTarget)
-        main: @options.main
-        bottom: true
-      popup.render()
-
-
     augmentWithCoverage: (source) ->
       coverage = @model.get 'coverage'
       if coverage
index 74e9e25a91faf95b66a821e80ed6798c46692ac2..26291e3b53a1c1cf1a2f1b36713b3299807f1f35 100644 (file)
@@ -13,3 +13,17 @@ define [
 
   class TimeChangesPopupView extends Popup
     template: Templates['time-changes-popup']
+
+
+    events:
+      'click a[data-period]': 'enablePeriod'
+
+
+    enablePeriod: (e) ->
+      period = $(e.currentTarget).data 'period'
+      @options.main.enablePeriod period
+
+
+    serializeData: ->
+      component: @options.main.component.toJSON()
+      periods: @options.main.periods.toJSON()
index aa2a911de8f4b2c172cec6c26d0c83d03caf1dd3..d2bdb1f00bf496e7ac62f47dfc9e91d47577f398 100644 (file)
@@ -3,4 +3,17 @@ define ->
   splitLongName: (longName) ->
     lastSeparator = longName.lastIndexOf '/'
     dir: longName.substr 0, lastSeparator
-    name: longName.substr lastSeparator + 1
\ No newline at end of file
+    name: longName.substr lastSeparator + 1
+
+
+  sortSeverities: (severities) ->
+    order = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
+    _.sortBy severities, (s) -> order.indexOf s.key
+
+
+  mixOf: (base, mixins...) ->
+    class Mixed extends base
+    for mixin in mixins by -1 # earlier mixins override later ones
+      for name, method of mixin::
+        Mixed::[name] = method
+    Mixed
\ No newline at end of file
index 0eb219181c950284baab665edcf7d3848a7d1368..0adfde9f815978acec067ac2942c42712c5b9879 100644 (file)
@@ -16,7 +16,7 @@
 
   <div class="component-viewer-header-links">
     <a><i class="icon-link"></i></a>
-    {{#if component.canMarkAsFavourite}}
+    {{#if state.canMarkAsFavourite}}
       <a class="js-favorite"
          title="{{#if component.fav}}{{t 'click_to_remove_from_favorites'}}{{else}}{{t 'click_to_add_to_favorites'}}{{/if}}">
         <i class="{{#if component.fav}}icon-favorite{{else}}icon-not-favorite{{/if}}"></i>
@@ -25,7 +25,7 @@
   </div>
 
   <div class="component-viewer-header-measures">
-    {{#eq component.q 'UTS'}}
+    {{#if component.isUnitTest}}
       <div class="component-viewer-header-measures-scope">
         <a data-scope="tests" class="component-viewer-header-measures-expand">
           <div class="component-viewer-header-measure">
@@ -35,9 +35,9 @@
           <i class="icon-dropdown"></i>
         </a>
       </div>
-    {{/eq}}
+    {{/if}}
 
-    {{#notEq component.q 'UTS'}}
+    {{#unless component.isUnitTest}}
       <div class="component-viewer-header-measures-scope">
         <span data-scope="basic" class="js-toggle-coverage component-viewer-header-measures-toggle-scope inactive"></span>
         <a data-scope="basic" class="component-viewer-header-measures-expand">
@@ -48,7 +48,7 @@
           <i class="icon-dropdown"></i>
         </a>
       </div>
-    {{/notEq}}
+    {{/unless}}
 
     <div class="component-viewer-header-measures-scope">
       <a data-scope="issues" class="component-viewer-header-measures-expand">
@@ -84,7 +84,7 @@
          class="js-toggle-issues component-viewer-header-measures-toggle-scope {{#if settings.issues}}active{{/if}}"></a>
     </div>
 
-    {{#notEq component.q 'UTS'}}
+    {{#unless component.isUnitTest}}
       {{#if component.measures.fCoverage}}
         <div class="component-viewer-header-measures-scope">
           <a data-scope="coverage" class="component-viewer-header-measures-expand">
              class="js-toggle-duplications component-viewer-header-measures-toggle-scope {{#if settings.duplications}}active{{/if}}"></a>
         </div>
       {{/if}}
-    {{/notEq}}
+    {{/unless}}
 
     <div class="component-viewer-header-measures-scope">
       <span data-scope="scm" class="component-viewer-header-measures-expand">
          class="js-toggle-scm component-viewer-header-measures-toggle-scope {{#if settings.scm}}active{{/if}}"></a>
     </div>
   </div>
-</div>
-
-
-
-<div data-scope="basic" class="component-viewer-header-expanded-bar">
-  {{> '_basic-header'}}
-</div>
-
-<div data-scope="issues" class="component-viewer-header-expanded-bar">
-  {{> '_issues-header'}}
-</div>
 
-<div data-scope="coverage" class="component-viewer-header-expanded-bar">
-  {{> '_coverage-header'}}
-</div>
-
-<div data-scope="duplications" class="component-viewer-header-expanded-bar">
-  {{> '_duplications-header'}}
-</div>
-
-{{#eq component.q 'UTS'}}
-  <div data-scope="tests" class="component-viewer-header-expanded-bar">
-    {{> '_tests-header'}}
+  <div class="component-viewer-header-time-changes">
+    <a>
+      <i class="icon-period"></i>
+      <div>{{#if period.key}}{{period.label}}{{else}}Time<br>Changes{{/if}}</div>
+    </a>
   </div>
-{{/eq}}
-
-
+</div>
 
-<div class="component-viewer-header-decoration" style="display: none;">
-  <div class="button-group">
-    <button class="js-toggle-issues {{#if settings.issues}}active{{/if}}">Issues</button>
-    <button class="js-toggle-coverage {{#if settings.coverage}}active{{/if}}">Coverage</button>
-    <button class="js-toggle-duplications {{#if settings.duplications}}active{{/if}}">Duplications</button>
-    <button class="js-toggle-scm {{#if settings.scm}}active{{/if}}">SCM</button>
-  </div>
+<div class="component-viewer-header-expanded-bar">
+  <i class="spinner spinner-margin"></i>
 </div>
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/_basic-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/_basic-header.hbs
deleted file mode 100644 (file)
index b7725e3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-{{#with component.msr}}
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.size'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderItem lines 'lines'}}}
-      {{{componentViewerHeaderItem ncloc 'ncloc'}}}
-    </ul>
-  </div>
-
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.complexity'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderItem complexity 'complexity'}}}
-      {{{componentViewerHeaderItem function_complexity 'function_complexity'}}}
-    </ul>
-  </div>
-
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.structure'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderItem classes 'classes'}}}
-      {{{componentViewerHeaderItem functions 'functions'}}}
-      {{{componentViewerHeaderItem accessors 'accessors'}}}
-      {{{componentViewerHeaderItem statements 'statements'}}}
-    </ul>
-  </div>
-
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.documentation'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderItem comment_lines 'comment_lines'}}}
-      {{{componentViewerHeaderItem comment_lines_density 'comment_lines_density'}}}
-      {{{componentViewerHeaderItem public_api 'public_api'}}}
-      {{{componentViewerHeaderItem public_undocumented_api 'public_undocumented_api'}}}
-      {{{componentViewerHeaderItem public_documented_api_density 'public_documented_api_density'}}}
-    </ul>
-  </div>
-{{/with}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/_coverage-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/_coverage-header.hbs
deleted file mode 100644 (file)
index 5df42dc..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-{{#with component.msr}}
-  <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 covered_lines 'covered_lines' 'js-filter-covered-lines'}}}
-      {{{componentViewerHeaderLink uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines'}}}
-    </ul>
-  </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 covered_conditions 'covered_conditions' 'js-filter-covered-branches'}}}
-        {{{componentViewerHeaderLink uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches'}}}
-      </ul>
-    </div>
-  {{/any}}
-
-
-  {{#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_covered_lines 'covered_lines' 'js-filter-covered-lines-it'}}}
-        {{{componentViewerHeaderLink it_uncovered_lines 'uncovered_lines' 'js-filter-uncovered-lines-it'}}}
-      </ul>
-    </div>
-  {{/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_covered_conditions 'covered_conditions' 'js-filter-covered-branches-it'}}}
-        {{{componentViewerHeaderLink it_uncovered_conditions 'uncovered_conditions' 'js-filter-uncovered-branches-it'}}}
-      </ul>
-    </div>
-  {{/any}}
-{{/with}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/_duplications-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/_duplications-header.hbs
deleted file mode 100644 (file)
index 0497440..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{{#with component.msr}}
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">{{t 'duplications'}}</div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{{componentViewerHeaderLink duplicated_blocks 'duplicated_blocks' 'js-filter-duplications'}}}
-      {{{componentViewerHeaderLink duplicated_lines 'duplicated_lines' 'js-filter-duplications'}}}
-    </ul>
-  </div>
-{{/with}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/_issues-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/_issues-header.hbs
deleted file mode 100644 (file)
index 5448328..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-{{#if component.measures.fIssues}}
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.severities'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{#each component.severities}}
-        <li><a class="item js-filter-{{this.[0]}}-issues">
-          <span>{{severityIcon this.[0]}} {{t 'severity' this.[0]}}</span>
-          <span class="number">{{this.[2]}}</span>
-          <i class="icon-chevron-right"></i>
-        </a></li>
-      {{/each}}
-    </ul>
-  </div>
-{{/if}}
-
-{{#if component.measures.fIssues}}
-  <div class="component-viewer-header-expanded-bar-section">
-    <div class="component-viewer-header-expanded-bar-section-title">
-      {{t 'component_viewer.measure_section.rules'}}
-    </div>
-    <ul class="component-viewer-header-expanded-bar-section-list">
-      {{#each component.rules}}
-        <li><a class="item js-filter-rule" data-rule="{{this.[0]}}" title="{{this.[1]}}">
-          <span>{{this.[1]}}</span>
-          <span class="number">{{this.[2]}}</span>
-          <i class="icon-chevron-right"></i>
-        </a></li>
-      {{/each}}
-    </ul>
-  </div>
-{{/if}}
-
-<div class="component-viewer-header-expanded-bar-section">
-  <div class="component-viewer-header-expanded-bar-section-title">
-    {{t 'component_viewer.measure_section.issues'}}
-  </div>
-  <ul class="component-viewer-header-expanded-bar-section-list">
-    {{#if currentIssue}}
-      <li><a class="item js-filter-current-issue">
-        <span>{{t 'component_viewer.issues.current_issue'}}</span>
-        <i class="icon-chevron-right"></i>
-      </a></li>
-    {{/if}}
-    {{#if component.measures.fIssues}}
-      <li><a class="item js-filter-unresolved-issues">
-        <span>{{t 'component_viewer.issues.unresolved_issues'}}</span>
-        <span class="number">{{component.measures.fIssues}}</span>
-        <i class="icon-chevron-right"></i>
-      </a></li>
-    {{/if}}
-    {{#if component.measures.fIssues}}
-      <li><a class="item js-filter-fixed-issues">
-        <span>{{t 'component_viewer.issues.fixed_issues'}}</span>
-        <i class="icon-chevron-right"></i>
-      </a></li>
-    {{/if}}
-    <li><a class="item js-filter-false-positive-issues">
-      <span>{{t 'component_viewer.issues.false_positive_issues'}}</span>
-      <span class="number">{{component.msr.false_positive_issues}}</span>
-      <i class="icon-chevron-right"></i>
-    </a></li>
-  </ul>
-</div>
-
-{{#if component.canBulkChange}}
-  <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-bulk-change">
-        <span><i class="icon-bulk-change"></i> {{t 'bulk_change'}}</span>
-      </a></li>
-    </ul>
-  </div>
-{{/if}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/_tests-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/_tests-header.hbs
deleted file mode 100644 (file)
index c19c750..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<div class="component-viewer-header-expanded-bar-section large">
-  <div class="component-viewer-header-expanded-bar-section-title justify">
-    <span class="ib">{{t 'component_viewer.measure_section.unit_tests'}}</span>
-    <span class="ib">Covered Lines</span>
-  </div>
-  <ul class="component-viewer-header-expanded-bar-section-list">
-    {{#each component.tests}}
-      <li><a class="item js-unit-test" data-name="{{name}}">
-        <span class="label">{{testStatusIcon status}} <span class="subtitle">{{durationInMs}}ms</span>
-          {{name}}</span>
-        {{#eq status 'OK'}}
-          <span class="number">{{coveredLines}}</span>
-        {{/eq}}
-        <i class="icon-chevron-right"></i>
-      </a></li>
-    {{/each}}
-  </ul>
-</div>
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/basic-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/basic-header.hbs
new file mode 100644 (file)
index 0000000..aafa59c
--- /dev/null
@@ -0,0 +1,46 @@
+{{#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.size'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{{componentViewerHeaderItem lines 'lines'}}}
+      {{{componentViewerHeaderItem ncloc 'ncloc'}}}
+    </ul>
+  </div>
+
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">
+      {{t 'component_viewer.measure_section.complexity'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{{componentViewerHeaderItem complexity 'complexity'}}}
+      {{{componentViewerHeaderItem function_complexity 'function_complexity'}}}
+    </ul>
+  </div>
+
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">
+      {{t 'component_viewer.measure_section.structure'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{{componentViewerHeaderItem classes 'classes'}}}
+      {{{componentViewerHeaderItem functions 'functions'}}}
+      {{{componentViewerHeaderItem accessors 'accessors'}}}
+      {{{componentViewerHeaderItem statements 'statements'}}}
+    </ul>
+  </div>
+
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">
+      {{t 'component_viewer.measure_section.documentation'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{{componentViewerHeaderItem comment_lines 'comment_lines'}}}
+      {{{componentViewerHeaderItem comment_lines_density 'comment_lines_density'}}}
+      {{{componentViewerHeaderItem public_api 'public_api'}}}
+      {{{componentViewerHeaderItem public_undocumented_api 'public_undocumented_api'}}}
+      {{{componentViewerHeaderItem public_documented_api_density 'public_documented_api_density'}}}
+    </ul>
+  </div>
+{{/with}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/coverage-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/coverage-header.hbs
new file mode 100644 (file)
index 0000000..2e2e4a4
--- /dev/null
@@ -0,0 +1,52 @@
+{{#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>
+
+  {{#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}}
+
+
+  {{#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>
+  {{/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
diff --git a/sonar-server/src/main/hbs/component-viewer/header/duplications-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/duplications-header.hbs
new file mode 100644 (file)
index 0000000..b0ffccc
--- /dev/null
@@ -0,0 +1,9 @@
+{{#with component.measures}}
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">{{t 'duplications'}}</div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{{componentViewerHeaderLink duplicated_blocks 'duplicated_blocks' 'js-filter-duplications'}}}
+      {{{componentViewerHeaderItem duplicated_lines 'duplicated_lines'}}}
+    </ul>
+  </div>
+{{/with}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/issues-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/issues-header.hbs
new file mode 100644 (file)
index 0000000..d35dbd7
--- /dev/null
@@ -0,0 +1,76 @@
+{{#if component.measures.fIssues}}
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">
+      {{t 'component_viewer.measure_section.severities'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{#each state.severities}}
+        <li><a class="item js-filter-{{key}}-issues">
+          <span>{{severityIcon key}} {{name}}</span>
+          <span class="number">{{count}}</span>
+          <i class="icon-chevron-right"></i>
+        </a></li>
+      {{/each}}
+    </ul>
+  </div>
+{{/if}}
+
+{{#if component.measures.fIssues}}
+  <div class="component-viewer-header-expanded-bar-section">
+    <div class="component-viewer-header-expanded-bar-section-title">
+      {{t 'component_viewer.measure_section.rules'}}
+    </div>
+    <ul class="component-viewer-header-expanded-bar-section-list">
+      {{#each state.rules}}
+        <li><a class="item js-filter-rule" data-rule="{{key}}" title="{{name}}">
+          <span>{{name}}</span>
+          <span class="number">{{count}}</span>
+          <i class="icon-chevron-right"></i>
+        </a></li>
+      {{/each}}
+    </ul>
+  </div>
+{{/if}}
+
+<div class="component-viewer-header-expanded-bar-section">
+  <div class="component-viewer-header-expanded-bar-section-title">
+    {{t 'component_viewer.measure_section.issues'}}
+  </div>
+  <ul class="component-viewer-header-expanded-bar-section-list">
+    {{#if currentIssue}}
+      <li><a class="item js-filter-current-issue">
+        <span>{{t 'component_viewer.issues.current_issue'}}</span>
+        <i class="icon-chevron-right"></i>
+      </a></li>
+    {{/if}}
+    {{#if component.measures.fIssues}}
+      <li><a class="item js-filter-unresolved-issues">
+        <span>{{t 'component_viewer.issues.unresolved_issues'}}</span>
+        <span class="number">{{component.measures.fIssues}}</span>
+        <i class="icon-chevron-right"></i>
+      </a></li>
+    {{/if}}
+    {{#if component.measures.fIssues}}
+      <li><a class="item js-filter-fixed-issues">
+        <span>{{t 'component_viewer.issues.fixed_issues'}}</span>
+        <i class="icon-chevron-right"></i>
+      </a></li>
+    {{/if}}
+    <li><a class="item js-filter-false-positive-issues">
+      <span>{{t 'component_viewer.issues.false_positive_issues'}}</span>
+      <span class="number">{{component.measures.false_positive_issues}}</span>
+      <i class="icon-chevron-right"></i>
+    </a></li>
+  </ul>
+</div>
+
+{{#if state.canBulkChange}}
+  <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-bulk-change">
+        <span><i class="icon-bulk-change"></i> {{t 'bulk_change'}}</span>
+      </a></li>
+    </ul>
+  </div>
+{{/if}}
\ No newline at end of file
diff --git a/sonar-server/src/main/hbs/component-viewer/header/tests-header.hbs b/sonar-server/src/main/hbs/component-viewer/header/tests-header.hbs
new file mode 100644 (file)
index 0000000..c19c750
--- /dev/null
@@ -0,0 +1,18 @@
+<div class="component-viewer-header-expanded-bar-section large">
+  <div class="component-viewer-header-expanded-bar-section-title justify">
+    <span class="ib">{{t 'component_viewer.measure_section.unit_tests'}}</span>
+    <span class="ib">Covered Lines</span>
+  </div>
+  <ul class="component-viewer-header-expanded-bar-section-list">
+    {{#each component.tests}}
+      <li><a class="item js-unit-test" data-name="{{name}}">
+        <span class="label">{{testStatusIcon status}} <span class="subtitle">{{durationInMs}}ms</span>
+          {{name}}</span>
+        {{#eq status 'OK'}}
+          <span class="number">{{coveredLines}}</span>
+        {{/eq}}
+        <i class="icon-chevron-right"></i>
+      </a></li>
+    {{/each}}
+  </ul>
+</div>
\ No newline at end of file
index 5925fc12d038020e32e7a6746eddb93b9b0dcc7e..748bd4d914564b12a6f8a629ceaf8276bcebe9c6 100644 (file)
@@ -1,13 +1,9 @@
-<div class="component-viewer-popup-section">
-  <div class="component-viewer-popup-section-title">
-    Time changes
-  </div>
-  <ul class="component-viewer-popup-list">
-    <li><a><span>Current</span></a></li>
-    <li><a><span>Δ since previous analysis (Apr 02 2014)</span></a></li>
-    <li><a><span>Δ over 365 days (Apr 02 2013)</span></a></li>
-    <li><a><span>Δ since previous version (4.2 - Feb 24 2014)</span></a></li>
-  </ul>
-</div>
+<div class="component-viewer-popup-title">Time Changes</div>
+
+<ul class="component-viewer-popup-list">
+  {{#each periods}}
+    <li><a class="link-action" data-period="{{key}}">{{label}}</a></li>
+  {{/each}}
+</ul>
 
 <div class="component-viewer-popup-arrow"></div>
\ No newline at end of file
index 375cd59cb8dad5d3dfc9733383bc6b0b9ed46750..685a7f6f9c87baffdfb98a6f6237aeb6fcf828f8 100644 (file)
   & > a { display: block; }
 }
 
+.component-viewer-header-time-changes {
+  float: right;
+  max-width: 180px;
+
+  & > a {
+    display: block;
+    padding: 14px 10px;
+    border-right: 1px solid @barBorderColor;
+    font-size: @smallFontSize;
+    .trans;
+
+    &:hover { background-color: @barBorderColor; }
+
+    & > i {
+      display: block;
+      float: left;
+      margin-top: 8px;
+      margin-right: 4px;
+    }
+
+    & > div {
+      padding: 2px 0;
+      overflow: hidden;
+    }
+  }
+}
+
 .component-viewer-header-expanded-bar {
   display: none;
   .clearfix;
   border: 1px solid @barBorderColor;
   border-top: none;
   background-color: #fff;
+
+  &.active { display: block; }
 }
 
 .component-viewer-header-expanded-bar-section {
 
 .component-viewer-popup-list {
   margin-top: 5px;
+
+  & > li { padding: 2px 0; }
 }
 
 .component-viewer-popup-test {
 
 .component-viewer-popup-test-status {
   position: absolute;
-  top: 0; left: 0
+  left: 0;
+  top: 50%;
+  margin-top: -8px;
 }
 
 .component-viewer-popup-test-duration {
index d5032be70a7c6b084c99f82009e0a945cb21ba16..cd745ff2fc26eff75edb14937c9c03c4bf061fe6 100644 (file)
@@ -512,6 +512,10 @@ a[class^="icon-"], a[class*=" icon-"] {
   .rotate(240deg);
 }
 
+.spinner-margin {
+  margin: 10px;
+}
+
 @-webkit-keyframes spin {
   from { .rotate(0deg); }
   to { .rotate(360deg); }
index a2ed293ccec93652c8522d70c502f734e601e31e..ef2dc10a7b5f2d6361ca37e10aa2c4eff702679f 100644 (file)
       -ms-transform: rotate(@degrees);
           transform: rotate(@degrees);
 }
+.rotateX(@degrees) {
+  -webkit-transform: rotateX(@degrees);
+      -ms-transform: rotateX(@degrees);
+          transform: rotateX(@degrees);
+}
 .scale(@ratio) {
   -webkit-transform: scale(@ratio);
       -ms-transform: scale(@ratio);