]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5209 Issues time changes
authorStas Vilchik <vilchiks@gmail.com>
Tue, 10 Jun 2014 10:09:46 +0000 (16:09 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Tue, 10 Jun 2014 10:09:55 +0000 (16:09 +0600)
sonar-server/src/main/coffee/component-viewer/header.coffee
sonar-server/src/main/coffee/component-viewer/header/base-header.coffee
sonar-server/src/main/coffee/component-viewer/header/issues-header.coffee
sonar-server/src/main/coffee/component-viewer/header/tests-header.coffee
sonar-server/src/main/coffee/component-viewer/main.coffee
sonar-server/src/main/coffee/component-viewer/mixins/main-issues.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/issues-header.hbs
sonar-server/src/main/js/common/handlebars-extensions.js
sonar-server/src/main/less/component-viewer.less

index bbe11055e9a6f55dcd2a0813dd4fc56a26a23056..b6f84d4ba0f5a302d38551ec8a791ac4265a4e4e 100644 (file)
@@ -8,8 +8,6 @@ define [
   'component-viewer/header/duplications-header'
   'component-viewer/header/tests-header'
 
-  'component-viewer/time-changes-popup'
-
   'common/handlebars-extensions'
 ], (
   Marionette
@@ -20,8 +18,6 @@ define [
   CoverageHeaderView
   DuplicationsHeaderView
   TestsHeaderView
-
-  TimeChangesPopupView
 ) ->
 
   $ = jQuery
@@ -61,8 +57,6 @@ define [
       'click .js-toggle-duplications': 'toggleDuplications'
       'click .js-toggle-scm': 'toggleSCM'
 
-      'click .component-viewer-header-time-changes a': 'showTimeChangesPopup'
-
 
     initialize: (options) ->
       options.main.settings.on 'change', => @changeSettings()
@@ -73,6 +67,12 @@ define [
 
     onRender: ->
       @delegateEvents()
+      activeHeaderTab = @state.get 'activeHeaderTab'
+      activeHeaderItem = @state.get 'activeHeaderItem'
+      if activeHeaderTab
+        @enableBar(activeHeaderTab).done =>
+          if activeHeaderItem
+            @enableBarItem activeHeaderItem
 
 
     toggleFavorite: ->
@@ -120,10 +120,14 @@ define [
         @state.set 'activeHeaderTab', scope
         bar = _.findWhere BARS, scope: scope
         @barRegion.show new bar.view
-          state: @state, component: @component, settings: @settings, source: @model, header: @
+          main: @options.main, state: @state, component: @component, settings: @settings, source: @model, header: @
         @ui.expandLinks.filter("[data-scope=#{scope}]").addClass 'active'
 
 
+    enableBarItem: (item) ->
+      @$(item).click()
+
+
     showExpandedBar: (e) ->
       el = $(e.currentTarget)
       active = el.is '.active'
@@ -157,13 +161,8 @@ define [
     toggleWorkspace: (e) -> @toggleSetting e, @options.main.showWorkspace, @options.main.hideWorkspace
 
 
-    showTimeChangesPopup: (e) ->
-      e.stopPropagation()
-      $('body').click()
-      popup = new TimeChangesPopupView
-        triggerEl: $(e.currentTarget)
-        main: @options.main
-      popup.render()
+    showTimeChangesSpinner: ->
+      @$('.component-viewer-header-time-changes').html '<i class="spinner spinner-margin"></i>'
 
 
     filterLines: (e, methodName, extra) ->
@@ -188,5 +187,4 @@ define [
       state: @state.toJSON()
       showSettings: @showSettings
       component: component
-      currentIssue: @options.main.currentIssue
-      period: @options.main.period?.toJSON()
\ No newline at end of file
+      currentIssue: @options.main.currentIssue
\ No newline at end of file
index 2d870bf860f54370513e3ca29149f740d0449fed..d2ff873010446043008e3a8a1ad0a3b13091c5b4 100644 (file)
@@ -8,6 +8,7 @@ define [
 
     initialize: (options) ->
       super
+      @main = options.main
       @state = options.state
       @component = options.component
       @settings = options.settings
@@ -19,4 +20,5 @@ define [
       _.extend super,
         state: @state.toJSON()
         component: @component.toJSON()
-        settings: @settings.toJSON()
\ No newline at end of file
+        settings: @settings.toJSON()
+        periods: @main.periods.toJSON()
\ No newline at end of file
index 5699961552670b685bc74d41fe302897c32fe22b..13989aa78d15710ef96a4021e39172fc5d100112 100644 (file)
@@ -2,10 +2,12 @@ define [
   'backbone.marionette'
   'templates/component-viewer'
   'component-viewer/header/base-header'
+  'component-viewer/time-changes-popup'
 ], (
   Marionette
   Templates
   BaseHeaderView
+  TimeChangesPopupView
 ) ->
 
   $ = jQuery
@@ -17,6 +19,7 @@ define [
 
     events:
       'click .js-issues-bulk-change': 'issuesBulkChange'
+      'click .js-issues-time-changes': 'issuesTimeChanges'
 
       'click .js-filter-current-issue': 'filterByCurrentIssue'
       'click .js-filter-all-issues': 'filterByAllIssues'
@@ -38,16 +41,72 @@ define [
         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'
+    issuesTimeChanges: (e) ->
+      e.stopPropagation()
+      $('body').click()
+      popup = new TimeChangesPopupView
+        triggerEl: $(e.currentTarget)
+        main: @options.main
+        bottom: true
+      popup.render()
+      popup.on 'change', (period) => @main.enableIssuesPeriod period
 
-    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
+    filterByCurrentIssue: (e) ->
+      @header.filterLines e, 'filterByCurrentIssue'
+      @state.set 'activeHeaderItem', '.js-filter-current-issues'
+
+
+    filterByAllIssues: (e) ->
+      @header.filterLines e, 'filterByAllIssues'
+      @state.set 'activeHeaderItem', '.js-filter-all-issues'
+
+
+    filterByFixedIssues: (e) ->
+      @header.filterLines e, 'filterByFixedIssues'
+      @state.set 'activeHeaderItem', '.js-filter-fixed-issues'
+
+
+    filterByUnresolvedIssues: (e) ->
+      @header.filterLines e, 'filterByUnresolvedIssues'
+      @state.set 'activeHeaderItem', '.js-filter-unresolved-issues'
+
+
+    filterByFalsePositiveIssues: (e) ->
+      @header.filterLines e, 'filterByFalsePositiveIssues'
+      @state.set 'activeHeaderItem', '.js-filter-false-positive-issues'
+
+
+    filterByRule: (e) ->
+      rule = $(e.currentTarget).data 'rule'
+      @header.filterLines e, 'filterByRule', rule
+      @state.set 'activeHeaderItem', ".js-filter-rule[data-rule='#{rule}']"
+
+
+    filterByBlockerIssues: (e) ->
+      @header.filterLines e, 'filterByBlockerIssues'
+      @state.set 'activeHeaderItem', '.js-filter-BLOCKER-issues'
+
+
+    filterByCriticalIssues: (e) ->
+      @header.filterLines e, 'filterByCriticalIssues'
+      @state.set 'activeHeaderItem', '.js-filter-CRITICAL-issues'
+
+
+    filterByMajorIssues: (e) ->
+      @header.filterLines e, 'filterByMajorIssues'
+      @state.set 'activeHeaderItem', '.js-filter-MAJOR-issues'
+
+
+    filterByMinorIssues: (e) ->
+      @header.filterLines e, 'filterByMinorIssues'
+      @state.set 'activeHeaderItem', '.js-filter-MINOR-issues'
+
+
+    filterByInfoIssues: (e) ->
+      @header.filterLines e, 'filterByInfoIssues'
+      @state.set 'activeHeaderItem', '.js-filter-INFO-issues'
+
+
+    serializeData: ->
+      _.extend super, period: @state.get('issuesPeriod')?.toJSON()
\ No newline at end of file
index ddcebd2277d8cf97c85ed77c24ecfa76c5981727..002c4b21dbe4dcea67e3c70fea19c661e7561d3d 100644 (file)
@@ -46,5 +46,5 @@ define [
           triggerEl: $(e.currentTarget)
           collection: new Backbone.Collection data.files
           test: test
-          main: @options.main
+          main: @main
         popup.render()
\ No newline at end of file
index 11f54131d602d401e3d9086d3083f30e3506ba65..ada2780e0a0b59bdfa07b6c8c485db04894823ea 100644 (file)
@@ -98,7 +98,6 @@ define [
         model: @source
         main: @
 
-      @period = null
       @periods = new Backbone.Collection [], model: Period
 
 
@@ -123,8 +122,8 @@ define [
       @headerRegion.show @headerView
 
 
-    requestComponent: (key, clear = false) ->
-      STATE_FIELDS = ['canBulkChange', 'canMarkAsFavourite', 'scmAvailable']
+    requestComponent: (key, clear = false, full = true) ->
+      STATE_FIELDS = ['canBulkChange', 'canMarkAsFavourite', 'tabs']
       COMPONENT_FIELDS = ['key', 'name', 'path', 'q', 'projectName', 'subProjectName', 'measures', 'fav']
 
       $.get API_COMPONENT, key: key, (data) =>
@@ -134,6 +133,7 @@ define [
         @component.set 'dir', utils.splitLongName(data.path).dir
         @component.set 'isUnitTest', data.q == 'UTS'
 
+
         # State
         stateAttributes = {}
         STATE_FIELDS.forEach (f) -> stateAttributes[f] = data[f]
@@ -141,13 +141,16 @@ define [
         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
+        if full
+          @state.clear silent: true
+          @state.set _.defaults stateAttributes, @state.defaults
+        else
+          @state.set stateAttributes
 
-        # Periods
-        @periods.reset [{}]
-        data.periods.forEach (p) => @periods.add key: p[0], label: p[1], sinceDate: new Date p[2]
-        @period = @periods.at 0
+        if full
+          # Periods
+          @periods.reset [{}]
+          data.periods.forEach (p) => @periods.add key: p[0], label: p[1], sinceDate: new Date p[2]
 
 
     requestMeasures: (key) ->
@@ -156,7 +159,8 @@ define [
         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) =>
+      data = resource: key, metrics: metrics
+      $.get API_MEASURES, data, (data) =>
         measuresList = data[0].msr || []
         measures = @component.get 'measures'
         measuresList.forEach (m) ->
@@ -215,11 +219,6 @@ define [
       @render()
 
 
-    enablePeriod: (period) ->
-      @period = @periods.findWhere key: period
-      @render()
-
-
     showAllLines: ->
       @sourceView.resetShowBlocks()
       @sourceView.showBlocks.push from: 0, to: _.size @source.get 'source'
index b840a274a5c25a94147867f9aa4ce1c7a119dd0b..d29235bc9ca8cd535e687e8d23ea13b402cb0611 100644 (file)
@@ -1,6 +1,7 @@
 define [], () ->
 
   $ = jQuery
+  API_COMPONENT = "#{baseUrl}/api/components/app"
   API_ISSUES = "#{baseUrl}/api/issues/search"
   LINES_AROUND_ISSUE = 4
 
@@ -35,6 +36,22 @@ define [], () ->
       @sourceView.render()
 
 
+    requestIssuesPeriod: (key, period) ->
+      $.get API_COMPONENT, key: key, period: period, (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
+
+
+    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()
+
+
     filterLinesByIssues: ->
       issues = @source.get 'issues'
       @sourceView.resetShowBlocks()
@@ -45,6 +62,12 @@ define [], () ->
 
 
     filterByIssues: (predicate, requestIssues = true) ->
+      issuesPeriod = @state.get('issuesPeriod')
+      if issuesPeriod
+        p = predicate
+        predicate = (issue) =>
+          (new Date(issue.creationDate) >= issuesPeriod.get('sinceDate')) && p issue
+
       if requestIssues && !@state.get 'hasIssues'
         @requestIssues(@key).done => @_filterByIssues(predicate)
       else
@@ -68,6 +91,7 @@ define [], () ->
     # Current Issue
     filterByCurrentIssue: -> @filterByIssues ((issue) => issue.key == @currentIssue), false
 
+
     # All Issues
     filterByAllIssues: -> @filterByIssues -> true
 
index 26291e3b53a1c1cf1a2f1b36713b3299807f1f35..a677ee7dd5b5c4b1953a6b4e57d2fe4d6123d347 100644 (file)
@@ -21,7 +21,7 @@ define [
 
     enablePeriod: (e) ->
       period = $(e.currentTarget).data 'period'
-      @options.main.enablePeriod period
+      @trigger 'change', period
 
 
     serializeData: ->
index 0adfde9f815978acec067ac2942c42712c5b9879..a3733bf8285735ccbcb134af4df1d5db8da0e350 100644 (file)
@@ -42,7 +42,7 @@
         <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">
           <div class="component-viewer-header-measure">
-            <span class="component-viewer-header-measure-value">{{component.measures.fNcloc}}</span>
+            <span class="component-viewer-header-measure-value">{{default component.measures.fNcloc '–'}}</span>
             <span class="component-viewer-header-measure-label">{{t 'metric.ncloc.name'}}</span>
           </div>
           <i class="icon-dropdown"></i>
@@ -53,7 +53,7 @@
     <div class="component-viewer-header-measures-scope">
       <a data-scope="issues" class="component-viewer-header-measures-expand">
           <div class="component-viewer-header-measure">
-            <span class="component-viewer-header-measure-value">{{default component.measures.fDebt 0}}</span>
+            <span class="component-viewer-header-measure-value">{{default component.measures.fDebt '–'}}</span>
             <span class="component-viewer-header-measure-label">{{t 'component_viewer.header.debt'}}</span>
           </div>
         {{#if component.measures.fIssues}}
          class="js-toggle-issues component-viewer-header-measures-toggle-scope {{#if settings.issues}}active{{/if}}"></a>
     </div>
 
-    {{#unless component.isUnitTest}}
-      {{#if component.measures.fCoverage}}
-        <div class="component-viewer-header-measures-scope">
-          <a data-scope="coverage" class="component-viewer-header-measures-expand">
-              <div class="component-viewer-header-measure">
-                <span class="component-viewer-header-measure-value">{{component.measures.fCoverage}}</span>
-                <span class="component-viewer-header-measure-label">{{t 'metric.coverage.name'}}</span>
-              </div>
-            <i class="icon-dropdown"></i>
-          </a>
-          <a data-scope="coverage" title="{{t 'component_viewer.header.toggle_coverage'}}"
-             class="js-toggle-coverage component-viewer-header-measures-toggle-scope {{#if settings.coverage}}active{{/if}}"></a>
-        </div>
-      {{/if}}
-
-      {{#if component.measures.fDuplicationDensity}}
-        <div class="component-viewer-header-measures-scope">
-          <a data-scope="duplications" class="component-viewer-header-measures-expand">
-              <div class="component-viewer-header-measure">
-                <span class="component-viewer-header-measure-value">{{component.measures.fDuplicationDensity}}</span>
-                <span class="component-viewer-header-measure-label">{{t 'metric.duplicated_lines_density.name'}}</span>
-              </div>
-            <i class="icon-dropdown"></i>
-          </a>
-          <a data-scope="duplications" title="{{t 'component_viewer.header.toggle_duplications'}}"
-             class="js-toggle-duplications component-viewer-header-measures-toggle-scope {{#if settings.duplications}}active{{/if}}"></a>
-        </div>
-      {{/if}}
-    {{/unless}}
+    {{#inArray state.tabs 'coverage'}}
+      <div class="component-viewer-header-measures-scope">
+        <a data-scope="coverage" class="component-viewer-header-measures-expand">
+            <div class="component-viewer-header-measure">
+              <span class="component-viewer-header-measure-value">{{default component.measures.fCoverage '–'}}</span>
+              <span class="component-viewer-header-measure-label">{{t 'metric.coverage.name'}}</span>
+            </div>
+          <i class="icon-dropdown"></i>
+        </a>
+        <a data-scope="coverage" title="{{t 'component_viewer.header.toggle_coverage'}}"
+           class="js-toggle-coverage component-viewer-header-measures-toggle-scope {{#if settings.coverage}}active{{/if}}"></a>
+      </div>
+    {{/inArray}}
 
-    <div class="component-viewer-header-measures-scope">
-      <span 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'}}"
-         class="js-toggle-scm component-viewer-header-measures-toggle-scope {{#if settings.scm}}active{{/if}}"></a>
-    </div>
-  </div>
+    {{#inArray state.tabs 'duplications'}}
+      <div class="component-viewer-header-measures-scope">
+        <a data-scope="duplications" class="component-viewer-header-measures-expand">
+            <div class="component-viewer-header-measure">
+              <span class="component-viewer-header-measure-value">{{default component.measures.fDuplicationDensity '–'}}</span>
+              <span class="component-viewer-header-measure-label">{{t 'metric.duplicated_lines_density.name'}}</span>
+            </div>
+          <i class="icon-dropdown"></i>
+        </a>
+        <a data-scope="duplications" title="{{t 'component_viewer.header.toggle_duplications'}}"
+           class="js-toggle-duplications component-viewer-header-measures-toggle-scope {{#if settings.duplications}}active{{/if}}"></a>
+      </div>
+    {{/inArray}}
 
-  <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>
+    {{#inArray state.tabs 'scm'}}
+      <div class="component-viewer-header-measures-scope">
+        <span 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'}}"
+           class="js-toggle-scm component-viewer-header-measures-toggle-scope {{#if settings.scm}}active{{/if}}"></a>
+      </div>
+    {{/inArray}}
   </div>
 </div>
 
index d35dbd7114059c2ed2d34e8169809fdb5f4fefbb..c463283044005d7769aca7c5406a66cb990164eb 100644 (file)
@@ -1,4 +1,4 @@
-{{#if component.measures.fIssues}}
+{{#ifNotEmpty state.severities}}
   <div class="component-viewer-header-expanded-bar-section">
     <div class="component-viewer-header-expanded-bar-section-title">
       {{t 'component_viewer.measure_section.severities'}}
@@ -13,9 +13,9 @@
       {{/each}}
     </ul>
   </div>
-{{/if}}
+{{/ifNotEmpty}}
 
-{{#if component.measures.fIssues}}
+{{#ifNotEmpty state.rules}}
   <div class="component-viewer-header-expanded-bar-section">
     <div class="component-viewer-header-expanded-bar-section-title">
       {{t 'component_viewer.measure_section.rules'}}
@@ -30,7 +30,7 @@
       {{/each}}
     </ul>
   </div>
-{{/if}}
+{{/ifNotEmpty}}
 
 <div class="component-viewer-header-expanded-bar-section">
   <div class="component-viewer-header-expanded-bar-section-title">
         <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-unresolved-issues">
+      <span>{{t 'component_viewer.issues.unresolved_issues'}}</span>
+      <i class="icon-chevron-right"></i>
+    </a></li>
+    <li><a class="item js-filter-fixed-issues">
+      <span>{{t 'component_viewer.issues.fixed_issues'}}</span>
+      <i class="icon-chevron-right"></i>
+    </a></li>
     <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>
   </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>
+      <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>
+
       </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}}
\ No newline at end of file
index 8deac86c643fee2de0e6203f26106411f3c3b307..8d7ada4f5269e2ecda74254fa13ee82e7f0da88c 100644 (file)
@@ -79,7 +79,11 @@ define(['handlebars', 'moment'], function (Handlebars, moment) {
   });
 
   Handlebars.registerHelper('percent', function(value, total) {
-    return '' + ((value || 0) / total * 100) + '%';
+    if (total > 0) {
+      return '' + ((value || 0) / total * 100) + '%';
+    } else {
+      return '0%';
+    }
   });
 
   Handlebars.registerHelper('eq', function(v1, v2, options) {
@@ -303,4 +307,12 @@ define(['handlebars', 'moment'], function (Handlebars, moment) {
     }
   });
 
+  Handlebars.registerHelper('ifMeasureShouldBeShown', function(measure, period, options) {
+    if (measure != null || period != null) {
+      return options.fn(this);
+    } else {
+      return options.inverse(this);
+    }
+  });
+
 });
index 685a7f6f9c87baffdfb98a6f6237aeb6fcf828f8..98143dca2407d43daec24880a1cbe923ccc29c8d 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;
 
 .component-viewer-header-expanded-bar-section-actions {
   margin-left: 15px;
+
+  .component-viewer-header-expanded-bar-section-list > li {
+    padding: 4px 0;
+    line-height: 1.5;
+  }
 }
 
 .component-viewer-header-expanded-bar-section-title {