]> source.dussan.org Git - sonarqube.git/commitdiff
Merge branch 'grunt'
authorStas Vilchik <vilchiks@gmail.com>
Thu, 20 Mar 2014 13:57:11 +0000 (19:57 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 20 Mar 2014 13:57:11 +0000 (19:57 +0600)
Conflicts:
sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_detail_quality_profile_template.hbs.erb
sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_detail_template.hbs.erb
sonar-server/src/main/webapp/WEB-INF/app/views/coding_rules/templates/_coding_rules_list_item_template.hbs.erb
sonar-server/src/main/webapp/WEB-INF/app/views/quality_gates/templates/_quality_gate_detail_condition_template.hbs.erb
sonar-server/src/main/webapp/javascripts/coding-rules/mockjax.js
sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-detail-quality-profile-view.js
sonar-server/src/main/webapp/javascripts/coding-rules/views/coding-rules-detail-view.js
sonar-server/src/main/webapp/javascripts/quality-gate/collections/conditions.js
sonar-server/src/main/webapp/javascripts/quality-gate/views/quality-gate-detail-condition-view.js
sonar-server/src/main/webapp/stylesheets/coding-rules.css
sonar-server/src/main/webapp/stylesheets/icons.css

1  2 
sonar-server/src/main/webapp/coffee/coding-rules/mockjax.coffee
sonar-server/src/main/webapp/coffee/coding-rules/views/coding-rules-detail-quality-profile-view.coffee
sonar-server/src/main/webapp/coffee/coding-rules/views/coding-rules-detail-view.coffee
sonar-server/src/main/webapp/coffee/quality-gate/collections/conditions.coffee
sonar-server/src/main/webapp/coffee/quality-gate/views/quality-gate-detail-condition-view.coffee
sonar-server/src/main/webapp/js/navigator/filters/metric-filters.js
sonar-server/src/main/webapp/js/templates/quality-gates.js
sonar-server/src/main/webapp/less/coding-rules.less
sonar-server/src/main/webapp/less/icons.less
sonar-server/src/main/webapp/templates/coding-rules/coding-rules-detail-quality-profile.hbs
sonar-server/src/main/webapp/templates/quality-gates/quality-gate-detail-condition.hbs

index 0000000000000000000000000000000000000000,5f09d1a81d72efebf3a5148cd42ec38a7f7d26a1..eb78a3cfda35cbeea7d8b9aefcfe76b922628ca3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,284 +1,284 @@@
 -        'coding_rules.inherits': 'Inherits'
+ define ['jquery.mockjax'], ->
+   jQuery.mockjaxSettings.contentType = 'text/json';
+   jQuery.mockjaxSettings.responseTime = 250;
+   # GET /api/codingrules/app
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/app"
+     responseText: JSON.stringify
+       qualityprofiles: [
+         { key: 'sonarway', name: 'Sonar Way', parent: null },
+         { key: 'qualityprofile1', name: 'Quality Profile 1', parent: 'sonarway' },
+         { key: 'qualityprofile2', name: 'Quality Profile 2', parent: 'sonarway' },
+         { key: 'qualityprofile3', name: 'Quality Profile 3', parent: null },
+       ]
+       languages:
+         java: 'Java'
+         javascript: 'JavaScript'
+       repositories:
+         'checkstyle': 'Checkstyle'
+         'common-java': 'Common SonarQube'
+         'findbugs': 'FindBugs'
+         'pmd': 'PMD'
+         'pmd-unit-tests': 'PMD Unit Tests'
+         'squid': 'SonarQube'
+       statuses:
+         'BETA': 'Beta'
+         'DEPRECATED': 'Deprecated'
+         'READY': 'Ready'
+       tags:
+         'brain-overload': 'brain-overload'
+         'bug': 'bug'
+         'comment': 'comment'
+         'convention': 'convention'
+         'error-handling': 'error-handling'
+         'formatting': 'formatting'
+         'java8': 'java8'
+         'multithreading': 'multithreading'
+         'naming': 'naming'
+         'pitfall': 'pitfall'
+         'security': 'security'
+         'size': 'size'
+         'unused': 'unused'
+         'unused-code': 'unused-code'
+       messages:
+         'all': 'All'
+         'any': 'Any'
+         'apply': 'Apply'
+         'bold': 'Bold'
+         'bulk_change': 'Bulk Change'
+         'bulleted_point': 'Bulleted point'
+         'cancel': 'Cancel'
+         'change': 'Change'
+         'code': 'Code'
+         'delete': 'Delete'
+         'done': 'Done'
+         'edit': 'Edit'
+         'markdown.helplink': 'Markdown Help'
+         'moreCriteria': '+ More Criteria'
+         'search_verb': 'Search'
+         'severity': 'Severity'
+         'update': 'Update'
+         'severity.BLOCKER': 'Blocker'
+         'severity.CRITICAL': 'Critical'
+         'severity.MAJOR': 'Major'
+         'severity.MINOR': 'Minor'
+         'severity.INFO': 'Info'
+         'coding_rules.activate': 'Activate'
+         'coding_rules.activate_quality_profile': 'Activate Quality Profile'
+         'coding_rules.add_note': 'Add Note'
+         'coding_rules.available_since': 'Available Since'
+         'coding_rules.bulk_change': 'Bulk Change'
+         'coding_rules.extend_description': 'Extend Description'
+         'coding_rules.deactivate_quality_profile': 'Deactivate'
+         'coding_rules.found': 'Found'
++        'coding_rules._inherits': 'inherits'
+         'coding_rules.key': 'Key:'
+         'coding_rules.new_search': 'New Search'
+         'coding_rules.no_results': 'No Coding Rules'
+         'coding_rules.order': 'Order'
+         'coding_rules.ordered_by': 'Ordered By'
+         'coding_rules.original': 'Original:'
+         'coding_rules.page': 'Coding Rules'
+         'coding_rules.parameters': 'Parameters'
+         'coding_rules.parameters.default_value': 'Default Value:'
+         'coding_rules.quality_profiles': 'Quality Profiles'
+         'coding_rules.quality_profile': 'Quality Profile'
+         'coding_rules.repository': 'Repository:'
+         'coding_rules.revert_to_parent_definition': 'Revert to Parent Definition'
+         'coding_rules._rules': 'rule(s)'
+         'coding_rules.select_tag': 'Select Tag'
+         'coding_rules.filters.availableSince': 'Available Since'
+         'coding_rules.filters.description': 'Description'
+         'coding_rules.filters.in_quality_profile': 'In Quality Profile'
+         'coding_rules.filters.inheritance': 'Inheritance'
+         'coding_rules.filters.inheritance.inactive': 'Inheritance criteria is available when an inherited quality profile is selected'
+         'coding_rules.filters.inheritance.any': 'Any'
+         'coding_rules.filters.inheritance.not_inherited': 'Not Inherited'
+         'coding_rules.filters.inheritance.inherited': 'Inherited'
+         'coding_rules.filters.inheritance.overriden': 'Overriden'
+         'coding_rules.filters.key': 'Key'
+         'coding_rules.filters.language': 'Language'
+         'coding_rules.filters.name': 'Name'
+         'coding_rules.filters.out_of_quality_profile': 'Out of Quality Profile'
+         'coding_rules.filters.repository': 'Repository'
+         'coding_rules.filters.severity': 'Severity'
+         'coding_rules.filters.status': 'Status'
+         'coding_rules.filters.tag': 'Tag'
+         'coding_rules.sort.creation_date': 'Creation Date'
+         'coding_rules.sort.name': 'Name'
+   # GET /api/codingrules/search
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/search"
+     responseText: JSON.stringify
+       codingrules: [
+         {
+           name: 'Array designators "[]" should be located after the type in method signatures'
+           language: 'Java'
+           severity: 'MAJOR'
+           status: 'DEPRECATED'
+         },
+         {
+           name: 'Avoid Array Loops'
+           language: 'Java'
+           severity: 'CRITICAL'
+           status: 'READY'
+         },
+         {
+           name: 'Bad practice - Abstract class defines covariant compareTo() method'
+           language: 'Java'
+           severity: 'MAJOR'
+           status: 'READY'
+         },
+         {
+           name: 'Correctness - Use of class without a hashCode() method in a hashed data structure'
+           language: 'Java'
+           severity: 'MINOR'
+           status: 'BETA'
+         },
+         {
+           name: 'Useless Operation On Immutable'
+           language: 'Java'
+           severity: 'MAJOR'
+           status: 'READY'
+         }
+       ]
+       paging:
+         total: 5
+         fTotal: '5'
+   # GET /api/codingrules/show
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/show"
+     responseText: JSON.stringify
+       codingrule:
+         name: 'Array designators "[]" should be located after the type in method signatures'
+         language: 'Java'
+         tags: ['bug', 'comment', 'java8']
+         creationDate: '2013-10-15'
+         fCreationDate: 'Oct 15, 2013'
+         status: 'DEPRECATED'
+         repository: 'squid'
+         key: 'S1190'
+         parameters: [
+           { key: 'someParameter', type: 'INT', default: 4, description: 'Some parameter description' }
+           { key: 'boolParameter', type: 'BOOL', description: 'Bool parameter description' }
+         ]
+         description: '''
+             <p>
+             According to the Java Language Specification:
+             </p>
+             <pre>For compatibility with older versions of the Java SE platform,
+             the declaration of a method that returns an array is allowed to place (some or all of)
+             the empty bracket pairs that form the declaration of the array type after
+             the formal parameter list. This obsolescent syntax should not be used in new code.
+             </pre>
+             <p>The following code snippet illustrates this rule:</p>
+             <pre>public int getVector()[] { /* ... */ }    // Non-Compliant
+             public int[] getVector() { /* ... */ }    // Compliant
+             public int[] getMatrix()[] { /* ... */ }  // Non-Compliant
+             public int[][] getMatrix() { /* ... */ }  // Compliant
+             </pre>'''
+         extra: '''<p>This note is here <b>only for test purposes</b>.</p>'''
+         extraRaw: '''This note is here *only for test purposes*.'''
+         qualityProfiles: [
+           {
+             name: 'SonarWay'
+             key: 'sonarway'
+             severity: 'MINOR'
+             canDeactivate: true
+             canUpdate: true
+             parameters: [
+               { key: 'someParameter', value: 8 }
+             ]
+           },
+           {
+             name: 'Quality Profile 1'
+             key: 'qualityprofile1'
+             severity: 'MAJOR'
+             canDeactivate: false
+             canUpdate: false
+             parameters: [
+               { key: 'someParameter', value: 6 }
+             ]
+             inherits: 'sonarway'
+             note:
+               username: 'Admin Admin'
+               html: '''<p>This note is here <b>only for test purposes</b>.</p>'''
+               raw: '''This note is here *only for test purposes*.'''
+               fCreationDate: 'less than a minute'
+           }
+         ]
+   # POST /api/codingrules/extend_description
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/extend_description"
+     responseText: JSON.stringify
+       extra: '''<p>This note is here <i>only for test purposes</i>.</p>'''
+       extraRaw: '''This note is here *only for test purposes*.'''
+   # POST /api/codingrules/bulk_change
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/bulk_change"
+   # POST /api/codingrules/set_tags
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/set_tags"
+   # POST /api/codingrules/activate
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/activate"
+   # POST /api/codingrules/note
+   jQuery.mockjax
+     url: "#{baseUrl}/api/codingrules/note"
+     responseText: JSON.stringify
+       note:
+         username: 'Admin Admin'
+         html: '''<p>This note is here <b>only for test purposes</b>.</p>'''
+         raw: '''This note is here *only for test purposes*.'''
+         fCreationDate: 'less than a minute'
+   # GET /api/qualityprofiles/list
+   jQuery.mockjax
+     url: "#{baseUrl}/api/qualityprofiles/list"
+     responseText: JSON.stringify
+       more: false
+       results: [
+         { id: 'sonarway', text: 'Sonar Way', parent: null },
+         { id: 'qp1', text: 'Quality Profile 1', parent: 'sonarway' },
+         { id: 'qp2', text: 'Quality Profile 2', parent: 'sonarway' },
+         { id: 'qp3', text: 'Quality Profile 3', parent: null },
+       ]
+   # GET /api/qualityprofiles/show
+   jQuery.mockjax
+     url: "#{baseUrl}/api/qualityprofiles/show"
+     responseText: JSON.stringify
+       qualityprofile:
+         id: 'sonarway', text: 'Sonar Way', parent: null
index 0000000000000000000000000000000000000000,f0b2624a889d81234120dfa7158cb9cee6d0a07f..169984ba6b5134ae32fd78bd21abdef089b62398
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,99 +1,107 @@@
+ define [
+   'backbone.marionette',
+   'templates/coding-rules'
+ ], (
+   Marionette,
+   Templates
+ ) ->
+   class CodingRulesDetailQualityProfilesView extends Marionette.ItemView
+     className: 'coding-rules-detail-quality-profile'
+     template: Templates['coding-rules-detail-quality-profile']
+     ui:
++      update: '.coding-rules-detail-quality-profile-update'
+       severitySelect: '.coding-rules-detail-quality-profile-severity'
+       note: '.coding-rules-detail-quality-profile-note'
+       noteForm: '.coding-rules-detail-quality-profile-note-form'
+       noteText: '.coding-rules-detail-quality-profile-note-text'
+       noteAdd: '.coding-rules-detail-quality-profile-note-add'
+       noteEdit: '.coding-rules-detail-quality-profile-note-edit'
+       noteDelete: '.coding-rules-detail-quality-profile-note-delete'
+       noteCancel: '.coding-rules-detail-quality-profile-note-cancel'
+       noteSubmit: '.coding-rules-detail-quality-profile-note-submit'
+     events:
+       'click @ui.noteAdd': 'editNote'
+       'click @ui.noteEdit': 'editNote'
+       'click @ui.noteDelete': 'deleteNote'
+       'click @ui.noteCancel': 'cancelNote'
+       'click @ui.noteSubmit': 'submitNote'
++      'change .coding-rules-detail-parameters select': 'enableUpdate'
++      'keyup .coding-rules-detail-parameters input': 'enableUpdate'
++
+     editNote: ->
+       @ui.note.hide()
+       @ui.noteForm.show()
+       @ui.noteText.focus()
+     deleteNote: ->
+       @ui.noteText.val ''
+       @submitNote().done =>
+         @model.unset 'note'
+         @render()
+     cancelNote: ->
+       @ui.note.show()
+       @ui.noteForm.hide()
+     submitNote: ->
+       @ui.note.html '<i class="spinner"></i>'
+       @ui.noteForm.html '<i class="spinner"></i>'
+       jQuery.ajax
+         type: 'POST'
+         url: "#{baseUrl}/api/codingrules/note"
+         dataType: 'json'
+         data: text: @ui.noteText.val()
+       .done (r) =>
+         @model.set 'note', r.note
+         @render()
++    enableUpdate: ->
++      @ui.update.prop 'disabled', false
++
++
+     onRender: ->
+       @ui.noteForm.hide()
+       format = (state) ->
+         return state.text unless state.id
+         "<i class='icon-severity-#{state.id.toLowerCase()}'></i> #{state.text}"
+       @ui.severitySelect.val @model.get 'severity'
+       @ui.severitySelect.select2
+         width: '200px'
+         minimumResultsForSearch: 999
+         formatResult: format
+         formatSelection: format
+         escapeMarkup: (m) -> m
+     getParent: ->
+       return null unless @model.get 'inherits'
+       @options.qualityProfiles.findWhere(key: @model.get('inherits')).toJSON()
+     enhanceParameters: ->
+       parent = @getParent()
+       parameters = @model.get 'parameters'
+       return parameters unless parent
+       parameters.map (p) ->
+         _.extend p, original: _.findWhere(parent.parameters, key: p.key).value
+     serializeData: ->
+       _.extend super,
+         parent: @getParent()
+         parameters: @enhanceParameters()
+         severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
index 0000000000000000000000000000000000000000,86eceb9e65e6e0cbc0615f1bc1890826f8e05135..aa87be03ee1c59a8d5246e3696f3bcb1134340e3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,112 +1,112 @@@
 -        width: '500px'
+ define [
+   'backbone',
+   'backbone.marionette',
+   'coding-rules/views/coding-rules-detail-quality-profiles-view'
+   'templates/coding-rules'
+ ], (
+   Backbone,
+   Marionette,
+   CodingRulesDetailQualityProfilesView,
+   Templates
+ ) ->
+   class CodingRulesDetailView extends Marionette.Layout
+     template: Templates['coding-rules-detail']
+     regions:
+       qualityProfilesRegion: '#coding-rules-detail-quality-profiles'
+     ui:
+       tagsChange: '.coding-rules-detail-tags-change'
+       tagInput: '.coding-rules-detail-tag-input'
+       tagsEdit: '.coding-rules-detail-tag-edit'
+       tagsEditDone: '.coding-rules-detail-tag-edit-done'
+       tagsList: '.coding-rules-detail-tag-list'
+       descriptionExtra: '#coding-rules-detail-description-extra'
+       extendDescriptionLink: '#coding-rules-detail-extend-description'
+       extendDescriptionForm: '#coding-rules-detail-extend-description-form'
+       extendDescriptionSubmit: '#coding-rules-detail-extend-description-submit'
+       extendDescriptionText: '#coding-rules-detail-extend-description-text'
+       extendDescriptionSpinner: '#coding-rules-detail-extend-description-spinner'
+       cancelExtendDescription: '#coding-rules-detail-extend-description-cancel'
+       qualityProfileActivate: '#coding-rules-quality-profile-activate'
+     events:
+       'click @ui.tagsChange': 'changeTags'
+       'click @ui.tagsEditDone': 'editDone'
+       'click @ui.extendDescriptionLink': 'showExtendDescriptionForm'
+       'click @ui.cancelExtendDescription': 'hideExtendDescriptionForm'
+       'click @ui.extendDescriptionSubmit': 'submitExtendDescription'
+       'click @ui.qualityProfileActivate': 'activateQualityProfile'
+     initialize: (options) ->
+       @qualityProfilesView = new CodingRulesDetailQualityProfilesView
+         collection: new Backbone.Collection options.model.get 'qualityProfiles'
+     onRender: ->
+       @qualityProfilesRegion.show @qualityProfilesView
+       @ui.tagInput.select2
+         tags: _.difference @options.app.tags, @model.get 'tags'
++        width: '300px'
+       @ui.tagsEdit.hide()
+       @ui.extendDescriptionForm.hide()
+       @ui.extendDescriptionSpinner.hide()
+       qp = @options.app.getActiveQualityProfile()
+       @$('.coding-rules-detail-quality-profile').first().addClass 'active' if qp?
+     changeTags: ->
+       @ui.tagsEdit.show()
+       @ui.tagsList.hide()
+     editDone: ->
+       @ui.tagsEdit.html '<i class="spinner"></i>'
+       tags = @ui.tagInput.val()
+       jQuery.ajax
+         type: 'POST'
+         url: "#{baseUrl}/api/codingrules/set_tags"
+         data: tags: tags
+       .done =>
+           @model.set 'tags', tags.split ','
+           @render()
+     showExtendDescriptionForm: ->
+       @ui.descriptionExtra.hide()
+       @ui.extendDescriptionForm.show()
+     hideExtendDescriptionForm: ->
+       @ui.descriptionExtra.show()
+       @ui.extendDescriptionForm.hide()
+     submitExtendDescription: ->
+       @ui.extendDescriptionForm.hide()
+       @ui.extendDescriptionSpinner.show()
+       jQuery.ajax
+         type: 'POST'
+         url: "#{baseUrl}/api/codingrules/extend_description"
+         dataType: 'json'
+         data: text: @ui.extendDescriptionText.val()
+       .done (r) =>
+         @model.set extra: r.extra, extraRaw: r.extraRaw
+         @render()
+     activateQualityProfile: ->
+       @options.app.codingRulesQualityProfileActivationView.model = @model
+       @options.app.codingRulesQualityProfileActivationView.show()
index 0000000000000000000000000000000000000000,d5b76bc712dc7a9f88d3c324675228501cb29b7c..8899fc14405c8c23cd78878a2a08bbaa51702e29
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,10 +1,11 @@@
+ define [
+   'backbone',
+   'quality-gate/models/condition'
+ ], (
+   Backbone,
+   Condition
+ ) ->
+   class Conditions extends Backbone.Collection
+     model: Condition
++    comparator: 'metric'
index 0000000000000000000000000000000000000000,4182198ff251d6e2d8ec7347131815e475a7ddd7..e928b169b2580a68dfbd93107af484acb5bbdf84
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,114 +1,123 @@@
 -      _.extend super,
+ define [
+   'backbone.marionette',
+   'templates/quality-gates'
+ ], (
+   Marionette,
+   Templates
+ ) ->
+   class QualityGateDetailConditionView extends Marionette.ItemView
+     tagName: 'tr'
+     template: Templates['quality-gate-detail-condition']
+     spinner: '<i class="spinner"></i>'
+     modelEvents:
+       'change:id': 'render'
+     ui:
+       periodSelect: '[name=period]'
+       operatorSelect: '[name=operator]'
+       warningInput: '[name=warning]'
+       errorInput: '[name=error]'
+       actionsBox: '.quality-gate-condition-actions'
+       updateButton: '.update-condition'
+     events:
+       'click @ui.updateButton': 'saveCondition'
+       'click .delete-condition': 'deleteCondition'
+       'click .add-condition': 'saveCondition'
+       'click .cancel-add-condition': 'cancelAddCondition'
+       'keyup :input': 'enableUpdate'
+       'change :input': 'enableUpdate'
+     initialize: ->
+       @populateMetric()
+     populateMetric: ->
+       metricKey = @model.get('metric')
+       metric = _.findWhere @options.app.metrics, key: metricKey
++      if metric?
++        switch metric.type
++          when 'WORK_DUR' then metric.placeholder = '1d 7h 59min'
++          when 'RATING' then metric.placeholder = 'A'
+       @model.set { metric: metric }, { silent: true }
+       @model.set { isDiffMetric: metric.key.indexOf('new_') == 0 }, { silent: true }
+     onRender: ->
+       @ui.periodSelect.val @model.get('period') || '0'
+       @ui.operatorSelect.val @model.get('op')
+       @ui.warningInput.val @model.get('warning')
+       @ui.errorInput.val @model.get('error')
+       @ui.periodSelect.select2
+         allowClear: false
+         minimumResultsForSearch: 999
+         width: '200px'
+       @ui.operatorSelect.select2
+         allowClear: false
+         minimumResultsForSearch: 999
+         width: '150px'
+       @ui.periodSelect.select2('open') if @model.isNew()
+     showSpinner: ->
+       jQuery(@spinner).prependTo @ui.actionsBox
+       @ui.actionsBox.find(':not(.spinner)').hide()
+     hideSpinner: ->
+       @ui.actionsBox.find('.spinner').remove()
+       @ui.actionsBox.find(':not(.spinner)').show()
+     saveCondition: ->
+       @showSpinner()
+       @model.set
+         period: @ui.periodSelect.val()
+         op: @ui.operatorSelect.val()
+         warning: @ui.warningInput.val()
+         error: @ui.errorInput.val()
+       @model.save()
+         .always =>
+           @ui.updateButton.prop 'disabled', true
+           @hideSpinner()
+         .done =>
+           @options.collectionView.updateConditions()
+     deleteCondition: ->
+       if confirm t('quality_gates.delete_condition.confirm.message')
+         @showSpinner()
+         @model.delete().done =>
+           @options.collectionView.collection.remove @model
+           @options.collectionView.updateConditions()
+           @close()
+     cancelAddCondition: ->
+       @close()
+     enableUpdate: ->
+       @ui.updateButton.prop 'disabled', false
+     serializeData: ->
+       period = _.findWhere(@options.app.periods, key: this.model.get('period'))
++      data = _.extend super,
+         canEdit: @options.app.canEdit
+         periods: @options.app.periods
+         periodText: period?.text
++      unless @options.app.canEdit
++        _.extend data,
++          warning: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('warning')).originalVal()
++          error: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('error')).originalVal()
++      data
index 0000000000000000000000000000000000000000,b97c557654f7cede8c58ab43b24b80cdaa728575..f680d0e06e14678d1a4e860559818dd3b7fcd572
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,161 +1,169 @@@
+ define(['navigator/filters/base-filters', 'common/handlebars-extensions'], function (BaseFilters) {
+   var DetailsMetricFilterView = BaseFilters.DetailsFilterView.extend({
+     template: getTemplate('#metric-filter-template'),
+     events: {
+       'change :input': 'inputChanged'
+     },
+     inputChanged: function() {
+       var value = {
+         metric: this.$('[name=metric]').val(),
+         metricText: this.$('[name=metric] option:selected').text(),
+         period: this.$('[name=period]').val(),
+         periodText: this.$('[name=period] option:selected').text(),
+         op: this.$('[name=op]').val(),
+         opText: this.$('[name=op] option:selected').text(),
+         val: this.$('[name=val]').val(),
+         valText: this.$('[name=val]').originalVal()
+       };
+       this.updateDataType(value);
+       this.model.set('value', value);
+     },
+     updateDataType: function(value) {
+       var metric = _.find(window.SS.metrics, function(m) {
+         return m.metric.name === value.metric;
+       });
+       if (metric) {
+         this.$('[name=val]').data('type', metric.metric.val_type);
++        switch (metric.metric.val_type) {
++            case 'WORK_DUR':
++                this.$('[name=val]').prop('placeholder', '1d 7h 59min');
++                break;
++            case 'RATING':
++                this.$('[name=val]').prop('placeholder', 'A');
++                break;
++        }
+       }
+     },
+     onRender: function() {
+       var value = this.model.get('value') || {};
+       this.$('[name=metric]').val(value.metric).select2({
+         width: '100%',
+         placeholder: window.SS.phrases.metric
+       });
+       this.$('[name=period]').val(value.period || 0).select2({
+         width: '100%',
+         minimumResultsForSearch: 100
+       });
+       this.$('[name=op]').val(value.op || 'eq').select2({
+         width: '60px',
+         placeholder: '=',
+         minimumResultsForSearch: 100
+       });
+       this.updateDataType(value);
+       this.$('[name=val]').val(value.val);
+       this.inputChanged();
+     },
+     onShow: function() {
+       var select = this.$('[name=metric]');
+       if (!select.val()) {
+         select.select2('open');
+       }
+     }
+   });
+   return BaseFilters.BaseFilterView.extend({
+     initialize: function() {
+       BaseFilters.BaseFilterView.prototype.initialize.call(this, {
+         detailsView: DetailsMetricFilterView
+       });
+       this.groupMetrics();
+     },
+     groupMetrics: function() {
+       var metrics = _.map(this.model.get('metrics'), function (metric) {
+             return metric.metric;
+           }),
+           groupedMetrics =
+               _.sortBy(
+                   _.map(
+                       _.groupBy(metrics, 'domain'),
+                       function (metrics, domain) {
+                         return {
+                           domain: domain,
+                           metrics: _.sortBy(metrics, 'short_name')
+                         };
+                       }),
+                   'domain'
+               );
+       this.model.set('groupedMetrics', groupedMetrics);
+     },
+     renderValue: function() {
+       return this.isDefaultValue() ?
+           window.SS.phrases.notSet :
+           this.model.get('value').metricText + ' ' + this.model.get('value').opText + ' ' + this.model.get('value').valText;
+     },
+     renderInput: function() {
+       var that = this,
+           value = this.model.get('value');
+       if (_.isObject(value) && value.metric && value.op && value.val) {
+         _.each(['metric', 'period', 'op', 'val'], function(key) {
+           var v = value[key];
+           if (key === 'period' && v === '0') {
+             v = '';
+           }
+           $j('<input>')
+               .prop('name', that.model.get('property') + '_' + key)
+               .prop('type', 'hidden')
+               .css('display', 'none')
+               .val(v)
+               .appendTo(that.$el);
+         });
+       }
+     },
+     isDefaultValue: function() {
+       var value = this.model.get('value');
+       if (!_.isObject(value)) {
+         return true;
+       }
+       return !(value.metric && value.op && value.val);
+     },
+     restoreFromQuery: function(q) {
+       var that = this,
+           value = {};
+       _.each(['metric', 'period', 'op', 'val'], function(p) {
+         var property = that.model.get('property') + '_' + p,
+             pValue = _.findWhere(q, { key: property });
+         if (pValue && pValue.value) {
+           value[p] = pValue.value;
+         }
+       });
+       if (value && value.metric && value.op && value.val) {
+         this.model.set({
+           value: value,
+           enabled: true
+         });
+       }
+     }
+   });
+ });
index 0000000000000000000000000000000000000000,00e75e9344831ff92f4081a8de67b524c8ae32f5..47dd03166b62cc5618a49752fd1efd157dd479de
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,543 +1,547 @@@
 -    + "\" type=\"text\">\n  ";
+ define(['handlebars'], function(Handlebars) {
+ this["SS"] = this["SS"] || {};
+ this["SS"]["Templates"] = this["SS"]["Templates"] || {};
+ this["SS"]["Templates"]["quality-gate-actions"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
+ function program1(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n  <div class=\"navigator-header-actions button-group\">\n    <button id=\"quality-gate-add\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "add_verb", options) : helperMissing.call(depth0, "t", "add_verb", options)))
+     + "</button>\n  </div>\n";
+   return buffer;
+   }
+   buffer += "<h1 class=\"navigator-header-title\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.page", options) : helperMissing.call(depth0, "t", "quality_gates.page", options)))
+     + "</h1>\n";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail-condition"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function", self=this;
+ function program1(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n    <select name=\"period\">\n      ";
+   stack1 = helpers.unless.call(depth0, (depth0 && depth0.isDiffMetric), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n      ";
+   stack1 = helpers.each.call(depth0, (depth0 && depth0.periods), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    </select>\n  ";
+   return buffer;
+   }
+ function program2(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<option value=\"0\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "value", options) : helperMissing.call(depth0, "t", "value", options)))
+     + "</option>";
+   return buffer;
+   }
+ function program4(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "<option value=\"";
+   if (helper = helpers.key) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.key); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\">&Delta; ";
+   if (helper = helpers.text) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.text); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "</option>";
+   return buffer;
+   }
+ function program6(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n    ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.periodText), {hash:{},inverse:self.program(9, program9, data),fn:self.program(7, program7, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n  ";
+   return buffer;
+   }
+ function program7(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "&Delta; ";
+   if (helper = helpers.periodText) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.periodText); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\n    ";
+   return buffer;
+   }
+ function program9(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "value", options) : helperMissing.call(depth0, "t", "value", options)))
+     + "\n    ";
+   return buffer;
+   }
+ function program11(depth0,data) {
+   
+   var buffer = "", stack1, helper, options;
+   buffer += "\n    <select name=\"operator\">\n      ";
+   stack1 = (helper = helpers.operators || (depth0 && depth0.operators),options={hash:{},inverse:self.noop,fn:self.program(12, program12, data),data:data},helper ? helper.call(depth0, ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.type), options) : helperMissing.call(depth0, "operators", ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.type), options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    </select>\n  ";
+   return buffer;
+   }
+ function program12(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n        <option value=\""
+     + escapeExpression((typeof depth0 === functionType ? depth0.apply(depth0) : depth0))
+     + "\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.operator", depth0, options) : helperMissing.call(depth0, "t", "quality_gates.operator", depth0, options)))
+     + "</option>\n      ";
+   return buffer;
+   }
+ function program14(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n    "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.operator", (depth0 && depth0.op), options) : helperMissing.call(depth0, "t", "quality_gates.operator", (depth0 && depth0.op), options)))
+     + "\n  ";
+   return buffer;
+   }
+ function program16(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n    <input name=\"warning\" class=\"measure-input\" data-type=\""
+     + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.type)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 -    + "\" type=\"text\">\n  ";
++    + "\" placeholder=\""
++    + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.placeholder)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
++    + "\"\n           type=\"text\">\n  ";
+   return buffer;
+   }
+ function program18(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "\n    ";
+   if (helper = helpers.warning) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.warning); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\n  ";
+   return buffer;
+   }
+ function program20(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n    <input name=\"error\" class=\"measure-input\" data-type=\""
+     + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.type)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
++    + "\" placeholder=\""
++    + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.placeholder)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
++    + "\"\n           type=\"text\">\n  ";
+   return buffer;
+   }
+ function program22(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "\n    ";
+   if (helper = helpers.error) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.error); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\n  ";
+   return buffer;
+   }
+ function program24(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n    ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.id), {hash:{},inverse:self.program(27, program27, data),fn:self.program(25, program25, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n  ";
+   return buffer;
+   }
+ function program25(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n      <div class=\"button-group\">\n        <button class=\"update-condition\" disabled>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "update_verb", options) : helperMissing.call(depth0, "t", "update_verb", options)))
+     + "</button>\n        <button class=\"button-red delete-condition\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "delete", options) : helperMissing.call(depth0, "t", "delete", options)))
+     + "</button>\n      </div>\n    ";
+   return buffer;
+   }
+ function program27(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n      <div class=\"button-group\">\n        <button class=\"add-condition\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "add_verb", options) : helperMissing.call(depth0, "t", "add_verb", options)))
+     + "</button>\n        <a class=\"action cancel-add-condition\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "cancel", options) : helperMissing.call(depth0, "t", "cancel", options)))
+     + "</a>\n      </div>\n    ";
+   return buffer;
+   }
+   buffer += "<td nowrap>\n  "
+     + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.metric)),stack1 == null || stack1 === false ? stack1 : stack1.name)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
+     + "\n</td>\n<td width=\"10%\" nowrap>\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.program(6, program6, data),fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n</td>\n<td width=\"10%\" nowrap>\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.program(14, program14, data),fn:self.program(11, program11, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n</td>\n<td width=\"15%\" nowrap=\"nowrap\">\n  <i class=\"icon-alert-warn\" title=\""
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "alerts.warning_tooltip", options) : helperMissing.call(depth0, "t", "alerts.warning_tooltip", options)))
+     + "\"></i>\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.program(18, program18, data),fn:self.program(16, program16, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n</td>\n<td width=\"15%\" nowrap=\"nowrap\">\n  <i class=\"icon-alert-error\" title=\""
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "alerts.error_tooltip", options) : helperMissing.call(depth0, "t", "alerts.error_tooltip", options)))
+     + "\"></i>\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.program(22, program22, data),fn:self.program(20, program20, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n</td>\n<td class=\"quality-gate-condition-actions\" width=\"120px\" nowrap>\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.noop,fn:self.program(24, program24, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n</td>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail-conditions-empty"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+   buffer += "<td colspan=\"6\">\n  "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.no_conditions", options) : helperMissing.call(depth0, "t", "quality_gates.no_conditions", options)))
+     + "\n</td>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail-conditions"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, self=this, helperMissing=helpers.helperMissing;
+ function program1(depth0,data) {
+   
+   var buffer = "", stack1, helper, options;
+   buffer += "\n  <div class=\"quality-gate-new-condition\">\n    <label for=\"quality-gate-new-condition-metric\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.add_condition", options) : helperMissing.call(depth0, "t", "quality_gates.add_condition", options)))
+     + ":</label>\n    <select id=\"quality-gate-new-condition-metric\">\n      <option></option>\n      ";
+   stack1 = helpers.each.call(depth0, (depth0 && depth0.metricGroups), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    </select>\n  </div>\n";
+   return buffer;
+   }
+ function program2(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "\n        <optgroup label=\"";
+   if (helper = helpers.domain) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.domain); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\">\n            ";
+   stack1 = helpers.each.call(depth0, (depth0 && depth0.metrics), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n        </optgroup>\n      ";
+   return buffer;
+   }
+ function program3(depth0,data) {
+   
+   var buffer = "", stack1, helper;
+   buffer += "<option value=\"";
+   if (helper = helpers.key) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.key); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "\">";
+   if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "</option>";
+   return buffer;
+   }
+   buffer += "<div class=\"quality-gate-section-name\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.conditions", options) : helperMissing.call(depth0, "t", "quality_gates.conditions", options)))
+     + "</div>\n\n<div class=\"quality-gate-introduction\">\n  <p>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.introduction", options) : helperMissing.call(depth0, "t", "quality_gates.introduction", options)))
+     + "\n    <a class=\"link-action quality-gate-introduction-show-more\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "more", options) : helperMissing.call(depth0, "t", "more", options)))
+     + "</a>\n  </p>\n  <div class=\"quality-gate-introduction-more inline-help\">\n    "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.health_icons", options) : helperMissing.call(depth0, "t", "quality_gates.health_icons", options)))
+     + "\n    <ul>\n      <li>\n        <i class=\"icon-alert-ok\"></i>\n        "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "alerts.notes.ok", options) : helperMissing.call(depth0, "t", "alerts.notes.ok", options)))
+     + "\n      </li>\n      <li>\n        <i class=\"icon-alert-warn\"></i>\n        "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "alerts.notes.warn", options) : helperMissing.call(depth0, "t", "alerts.notes.warn", options)))
+     + "\n      </li>\n      <li>\n        <i class=\"icon-alert-error\"></i>\n        "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "alerts.notes.error", options) : helperMissing.call(depth0, "t", "alerts.notes.error", options)))
+     + "\n      </li>\n    </ul>\n  </div>\n</div>\n\n";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n\n<table class=\"data zebra width100 marginbottom10 spaced quality-gate-conditions\">\n  <thead><tr></tr></thead>\n  <tbody></tbody>\n</table>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail-header"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this, functionType="function";
+ function program1(depth0,data) {
+   
+   var buffer = "", stack1, helper, options;
+   buffer += "\n  <div class=\"navigator-header-actions button-group\">\n    <button id=\"quality-gate-rename\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "rename", options) : helperMissing.call(depth0, "t", "rename", options)))
+     + "</button>\n    <button id=\"quality-gate-copy\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "copy", options) : helperMissing.call(depth0, "t", "copy", options)))
+     + "</button>\n    ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0['default']), {hash:{},inverse:self.program(4, program4, data),fn:self.program(2, program2, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    <button id=\"quality-gate-delete\" class=\"button-red\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "delete", options) : helperMissing.call(depth0, "t", "delete", options)))
+     + "</button>\n  </div>\n";
+   return buffer;
+   }
+ function program2(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n      <button id=\"quality-gate-unset-as-default\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "unset_as_default", options) : helperMissing.call(depth0, "t", "unset_as_default", options)))
+     + "</button>\n    ";
+   return buffer;
+   }
+ function program4(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n      <button id=\"quality-gate-set-as-default\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "set_as_default", options) : helperMissing.call(depth0, "t", "set_as_default", options)))
+     + "</button>\n    ";
+   return buffer;
+   }
+   buffer += "<h1 class=\"navigator-header-title\">";
+   if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "</h1>\n\n";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail-projects"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;
+ function program1(depth0,data) {
+   
+   var buffer = "", stack1;
+   buffer += "\n  <p class=\"quality-gate-default-message\">\n  ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0.canEdit), {hash:{},inverse:self.program(4, program4, data),fn:self.program(2, program2, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n  </p>\n";
+   return buffer;
+   }
+ function program2(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n    "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.projects_for_default.edit", options) : helperMissing.call(depth0, "t", "quality_gates.projects_for_default.edit", options)))
+     + "\n  ";
+   return buffer;
+   }
+ function program4(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "\n    "
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.projects_for_default", options) : helperMissing.call(depth0, "t", "quality_gates.projects_for_default", options)))
+     + "\n  ";
+   return buffer;
+   }
+ function program6(depth0,data) {
+   
+   
+   return "\n  <div id=\"select-list-projects\"></div>\n";
+   }
+   buffer += "<div class=\"quality-gate-section-name\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.projects", options) : helperMissing.call(depth0, "t", "quality_gates.projects", options)))
+     + "</div>\n\n";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0['default']), {hash:{},inverse:self.program(6, program6, data),fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-detail"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   
+   return "<div id=\"quality-gate-conditions\" class=\"quality-gate-section\"></div>\n<div id=\"quality-gate-projects\" class=\"quality-gate-section\"></div>";
+   });
+ this["SS"]["Templates"]["quality-gate-edit"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function", self=this;
+ function program1(depth0,data) {
+   
+   var buffer = "", stack1, helper, options;
+   buffer += "<h2>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.rename", options) : helperMissing.call(depth0, "t", "quality_gates.rename", options)))
+     + " ";
+   if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "</h2>";
+   return buffer;
+   }
+ function program3(depth0,data) {
+   
+   var buffer = "", stack1, helper, options;
+   buffer += "<h2>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.copy", options) : helperMissing.call(depth0, "t", "quality_gates.copy", options)))
+     + " ";
+   if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + "</h2>";
+   return buffer;
+   }
+ function program5(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<h2>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.add", options) : helperMissing.call(depth0, "t", "quality_gates.add", options)))
+     + "</h2>";
+   return buffer;
+   }
+ function program7(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<button>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "save", options) : helperMissing.call(depth0, "t", "save", options)))
+     + "</button>";
+   return buffer;
+   }
+ function program9(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<button>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "copy", options) : helperMissing.call(depth0, "t", "copy", options)))
+     + "</button>";
+   return buffer;
+   }
+ function program11(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<button>"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "create", options) : helperMissing.call(depth0, "t", "create", options)))
+     + "</button>";
+   return buffer;
+   }
+   buffer += "<form>\n  <div class=\"modal-head\">\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "rename", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "rename", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "copy", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "copy", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "create", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "create", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n  </div>\n\n  <div class=\"modal-body\">\n    <div class=\"modal-error\"></div>\n    <div class=\"modal-field\">\n      <label for=\"quality-gate-edit-name\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "name", options) : helperMissing.call(depth0, "t", "name", options)))
+     + " <em class=\"mandatory\">*</em></label>\n      <input id=\"quality-gate-edit-name\" type=\"text\" size=\"50\" maxlength=\"100\">\n    </div>\n  </div>\n\n  <div class=\"modal-foot\">\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "rename", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "rename", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(9, program9, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "copy", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "copy", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    ";
+   stack1 = (helper = helpers.eq || (depth0 && depth0.eq),options={hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.method), "create", options) : helperMissing.call(depth0, "eq", (depth0 && depth0.method), "create", options));
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "\n    <a id=\"quality-gate-cancel-create\" class=\"action\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "cancel", options) : helperMissing.call(depth0, "t", "cancel", options)))
+     + "</a>\n  </div>\n</form>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-sidebar-list-empty"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", helper, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+   buffer += "<div class=\"line line-nowrap\">"
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "quality_gates.noQualityGates", options) : helperMissing.call(depth0, "t", "quality_gates.noQualityGates", options)))
+     + "</div>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gate-sidebar-list-item"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   var buffer = "", stack1, helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function", self=this;
+ function program1(depth0,data) {
+   
+   var buffer = "", helper, options;
+   buffer += "<span class=\"subtitle\">("
+     + escapeExpression((helper = helpers.t || (depth0 && depth0.t),options={hash:{},data:data},helper ? helper.call(depth0, "default", options) : helperMissing.call(depth0, "t", "default", options)))
+     + ")</span>";
+   return buffer;
+   }
+   buffer += "<div class=\"line line-nowrap\">";
+   if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+   else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+   buffer += escapeExpression(stack1)
+     + " ";
+   stack1 = helpers['if'].call(depth0, (depth0 && depth0['default']), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+   if(stack1 || stack1 === 0) { buffer += stack1; }
+   buffer += "</div>";
+   return buffer;
+   });
+ this["SS"]["Templates"]["quality-gates-layout"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
+   this.compilerInfo = [4,'>= 1.0.0'];
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+   
+   return "<div class=\"navigator-header\"></div>\n<div class=\"navigator-results quality-gates-nav\"></div>\n<div class=\"navigator-details\"></div>\n<div class=\"navigator-actions\"></div>";
+   });
+ return this["SS"]["Templates"];
+ });
index 0000000000000000000000000000000000000000,6a3358b90aab233e68b787b7d58b05aea8593d6e..9669c34aceb11221ad7571c47454b65ac3aea648
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,165 +1,169 @@@
 -.coding-rules-detail-status {
 -  padding: 3px 4px;
 -  background-color: @navigatorBarBackground;
 -  color: #777;
 -  font-size: @smallFontSize;
 -}
 -
 -.coding-rules-detail-key {
 -
 -}
 -
+ @import 'mixins';
+ @import 'variables';
+ @import 'navigator/config';
+ .coding-rules-page {
+   .navigator-results .spinner {
+     margin: @navigatorPadding;
+   }
+ }
+ /*
+  * Detail
+  */
+ .coding-rules-detail-header,
+ .coding-rules-detail-title {
+   margin-bottom: @navigatorPadding;
+   line-height: 1.5;
+   font-weight: bold;
+ }
++.coding-rules-detail-header {
++  font-size: 18px;
++}
++
+ .coding-rules-detail-title {
+   display: inline-block;
++  margin-top: 3 * @navigatorPadding;
+   text-transform: uppercase;
+ }
 -  visibility: top;
+ .coding-rules-detail-properties {
+   margin: @navigatorPadding 0;
+   font-size: 0;
+ }
+ .coding-rules-detail-property {
+   display: inline-block;
+   vertical-align: middle;
+   font-size: @smallFontSize;
+   height: 22px;
+   line-height: 22px;
+   .select2-search-field { line-height: 1; }
+ }
+ .coding-rules-detail-property + .coding-rules-detail-property {
+   margin-left: 2 * @navigatorPadding;
+ }
+ .coding-rules-detail-tag + .coding-rules-detail-tag {
+   margin-left: @navigatorPadding;
+ }
+ .coding-rules-detail-description {
+   margin: 2 * @navigatorPadding 0;
+ }
+ .coding-rules-detail-description-extra {
+   margin-top: -@navigatorPadding;
+ }
+ .coding-rules-detail-parameters {
+   margin: @navigatorPadding 0 @navigatorPadding * 2;
+ }
+ .coding-rules-detail-parameter {
+   margin: @navigatorPadding 0;
+   font-size: 0;
+ }
+ .coding-rules-detail-parameter-name {
+   display: inline-block;
 -  font-weight: bold;
++  vertical-align: top;
+   width: 150px;
+   font-size: @baseFontSize;
 -  border: 1px solid transparent;
++  font-weight: normal;
+   text-align: right;
+ }
+ .coding-rules-detail-parameter-description {
+   display: inline-block;
+   vertical-align: top;
+   margin-left: 2 * @navigatorPadding;
+   font-size: @baseFontSize;
+ }
++.coding-rules-detail-parameter-original {
++  margin-left: @navigatorPadding;
++  font-size: @smallFontSize;
++}
++
+ // Quality Profiles
+ .coding-rules-detail-quality-profiles-header {
+   margin-top: 3 * @navigatorPadding;
+ }
+ .coding-rules-detail-quality-profiles {
+   font-size: 0;
+ }
+ .coding-rules-detail-quality-profile {
 -    border-color: @highlighted;
++  margin-left: 2 * @navigatorPadding;
+   &.active {
 -  padding: @navigatorPadding / 2;
 -  background-color: @navigatorBarBackground;
 -  line-height: 1;
+     .coding-rules-detail-quality-profile-name {
+       background-color: @highlighted;
+       color: #fff;
+     }
+   }
+ }
+ .coding-rules-detail-quality-profile + .coding-rules-detail-quality-profile {
+   margin-top: 2 * @navigatorPadding;
++  padding-top: 2 * @navigatorPadding;
++  border-top: 1px solid @navigatorBorderLightColor;
+ }
+ .coding-rules-detail-quality-profile-name {
 -  margin: @navigatorPadding 0;
 -  padding: 0 @navigatorPadding / 2;
+   font-weight: bold;
+ }
+ .coding-rules-detail-quality-profile-actions {
+   padding: @navigatorPadding / 2;
+ }
+ .coding-rules-detail-quality-profile-parameters {
+   padding: 0 @navigatorPadding / 2;
+   .coding-rules-detail-parameter-name,
+   .coding-rules-detail-parameter-description {
+     vertical-align: middle;
+   }
+   .coding-rules-detail-parameter-description input {
+     width: 200px;
+   }
+ }
+ .coding-rules-detail-quality-profile-inheritance {
 -  margin: @navigatorPadding 0;
++  vertical-align: middle;
++  margin-left: @navigatorPadding;
++  font-size: @smallFontSize;
++  font-weight: normal;
++  i {
++    position: relative;
++    top: -1px;
++    font-size: @iconSmallFontSize;
++  }
+   strong { font-weight: bold; }
+ }
+ .coding-rules-detail-quality-profile-note {
++  margin: @navigatorPadding * 2 0;
+   padding: 0 @navigatorPadding / 2;
+ }
+ .coding-rules-detail-quality-profiles-activation {
+   margin-top: -3px;
+   margin-left: @navigatorPadding;
+ }
index 0000000000000000000000000000000000000000,aca5c15c06d135d4b57d68b701709e1002ec6b91..5c8bc983bd16471feb1bba769b035239c72cbe4e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,341 +1,340 @@@
 -  font-size: @iconFontSize;
+ @import "variables";
+ @import "mixins";
+ @font-face {
+   font-family: 'sonar';
+   src: url('../fonts/sonar.woff') format('woff'),
+        url('../fonts/sonar.svg#sonar') format('svg');
+   font-weight: normal;
+   font-style: normal;
+ }
+ [class^="icon-"], [class*=" icon-"] {
+   font-family: 'sonar';
+   speak: none;
+   font-style: normal;
+   font-weight: normal;
+   font-variant: normal;
+   text-transform: none;
+   line-height: 1;
+   vertical-align: middle;
+   /* Better Font Rendering =========== */
+   -webkit-font-smoothing: antialiased;
+   -moz-osx-font-smoothing: grayscale;
+ }
+ a[class^="icon-"], a[class*=" icon-"] {
+   text-decoration: none !important;
+ }
+ /*
+  * Severity
+  */
+ [class^="icon-severity-"], [class*=" icon-severity"] {
+   position: relative;
+   top: -1px;
+ }
+ .icon-severity-blocker:before,
+ .icon-severity-4:before {
+   content: "\f000";
+   color: @severityBlockerColor;
+   font-size: @iconSmallFontSize;
+ }
+ .icon-severity-critical:before,
+ .icon-severity-3:before {
+   content: "\f001";
+   color: @severityCriticalColor;
+   font-size: @iconSmallFontSize;
+ }
+ .icon-severity-major:before,
+ .icon-severity-2:before {
+   content: "\f002";
+   color: @severityMajorColor;
+   font-size: @iconSmallFontSize;
+ }
+ .icon-severity-minor:before,
+ .icon-severity-1:before {
+   content: "\f003";
+   color: @severityMinorColor;
+   font-size: @iconSmallFontSize;
+ }
+ .icon-severity-info:before,
+ .icon-severity-0:before {
+   content: "\f004";
+   color: @severityInfoColor;
+   font-size: @iconSmallFontSize;
+ }
+ /*
+  * Status
+  */
+ [class^="icon-status-"], [class*=" icon-status"] {
+   position: relative;
+   top: -1px;
+ }
+ .icon-status-open:before {
+   content: "\f010";
+   color: @statusOpenColor;
+   font-size: @iconSmallFontSize;
+   line-height: @iconLineHeight;
+ }
+ .icon-status-confirmed:before {
+   content: "\f011";
+   color: @statusConfirmedColor;
+   font-size: @iconSmallFontSize;
+   line-height: @iconLineHeight;
+ }
+ .icon-status-reopened:before {
+   content: "\f012";
+   color: @statusReopenedColor;
+   font-size: @iconSmallFontSize;
+   line-height: @iconLineHeight;
+ }
+ .icon-status-resolved:before {
+   content: "\f013";
+   color: @statusResolvedColor;
+   font-size: @iconSmallFontSize;
+   line-height: @iconLineHeight;
+ }
+ .icon-status-closed:before {
+   content: "\f014";
+   color: @statusClosedColor;
+   font-size: @iconSmallFontSize;
+   line-height: @iconLineHeight;
+ }
+ /*
+  * Alert
+  */
+ .icon-alert-ok:before {
+   content: "\f013";
+   color: @green;
+   font-size: @iconFontSize;
+ }
+ .icon-alert-warn:before {
+   content: "\f000";
+   color: @orange;
+   font-size: @iconFontSize;
+ }
+ .icon-alert-error:before {
+   content: "\f057";
+   color: @red;
+   font-size: @iconFontSize;
+ }
+ .icon-alert-none:before {
+   content: "\f059";
+   color: @blue;
+   font-size: @iconFontSize;
+ }
+ /*
+  * Qualifier
+  */
+ .icon-qualifier-dir:before {
+   content: "\f07b";
+   font-size: @iconFontSize;
+ }
+ .icon-qualifier-fil:before {
+   content: "\f0f6";
+   font-size: @iconFontSize;
+ }
+ .icon-qualifier-lib:before {
+   content: "\e600";
+   font-size: @iconFontSize;
+ }
+ /*
+  * Common
+  */
+ .icon-list:before {
+   content: "\f039";
+ }
+ .icon-bullet-list:before {
+   content: "\f03a";
+ }
+ .icon-settings:before {
+   content: "\f015";
+ }
+ .icon-settings-multiple:before {
+   content: "\f085";
+ }
+ .icon-arrow-down:before {
+   content: "\f0d7";
+   position: relative;
+   top: -2px;
+ }
+ .icon-arrow-up:before {
+   content: "\f0d8";
+   position: relative;
+   top: -2px;
+ }
+ .icon-arrow-left:before {
+   content: "\f0d9";
+ }
+ .icon-arrow-right:before {
+   content: "\f0da";
+ }
+ .icon-emoticon-smiley:before {
+   content: "\f118";
+ }
+ .icon-emoticon-sad:before {
+   content: "\f119";
+ }
+ .icon-emoticon-speechless:before {
+   content: "\f11a";
+ }
+ .icon-rect-check:before {
+   content: "\f046";
+ }
+ .icon-check:before {
+   content: "\f00c";
+ }
+ .icon-default:before {
+   position: relative;
+   top: -0.1em;
+   content: "\f00c";
+ }
+ .icon-lang:before {
+   content: "\f024";
+   font-size: @iconSmallFontSize;
+ }
+ .icon-quality-profile:before {
+   content: "\f022";
+   font-size: @iconSmallFontSize;
+ }
+ .icon-tags:before {
+   content: "\f02c";
+   font-size: @iconSmallFontSize;
+ }
+ .icon-calendar:before {
+   position: relative;
+   top: -0.1em;
+   content: "\f073";
+   font-size: @iconSmallFontSize;
+ }
+ .icon-favorite:before {
+   content: "\f005";
+   color: @orange;
+   font-size: @iconFontSize;
+ }
+ .icon-not-favorite:before {
+   content: "\f005";
+   color: @darkGrey;
+   font-size: @iconFontSize;
+ }
+ .icon-help:before {
+   content: "\f059";
+   color: @blue;
+   font-size: @iconFontSize;
+ }
+ .icon-info:before {
+   content: "\f05a";
+   color: @blue;
+   font-size: @iconFontSize;
+ }
+ .icon-uniF060:before {
+   content: "\f060";
+ }
+ .icon-uniF061:before {
+   content: "\f061";
+ }
+ .icon-uniF062:before {
+   content: "\f062";
+ }
+ .icon-uniF063:before {
+   content: "\f063";
+ }
+ .icon-comment:before {
+   content: "\f075";
+ }
+ .icon-delete:before {
+   content: "\f00d";
+ }
+ .icon-compare:before {
+   content: "\f0c5";
+ }
+ .icon-link:before {
+   content: "\f0c1";
+ }
+ .icon-inheritance:before {
+   content: "\f126";
+ }
+ .icon-plus:before {
+   content: "\f067";
+ }
+ /*
+  * Spinner
+  */
+ .spinner {
+   position: relative;
+   vertical-align: middle;
+   .square(16px);
+   border: 2px solid #0cf;
+   border-radius: 50%;
+   .animation(spin 0.75s infinite linear);
+   // For IE9 only, because it does not support css animations
+   // Show animated gif
+   .ie9 & {
+     background-image: url(../images/loading.gif);
+     background-repeat: no-repeat;
+     background-position: 0 0;
+     border: none;
+   }
+ }
+ .spinner:before,
+ .spinner:after {
+   left: -2px;
+   top: -2px;
+   display: none;
+   position: absolute;
+   content: '';
+   width: inherit;
+   height: inherit;
+   border: inherit;
+   border-radius: inherit;
+ }
+ .spinner,
+ .spinner:before,
+ .spinner:after {
+   display: inline-block;
+   .box-sizing(border-box);
+   border-color: transparent;
+   border-top-color: @blue;
+   .animation-duration(1.2s);
+ }
+ .spinner:before {
+   .rotate(120deg);
+ }
+ .spinner:after {
+   .rotate(240deg);
+ }
+ @-webkit-keyframes spin {
+   from { .rotate(0deg); }
+   to { .rotate(360deg); }
+ }
+ @keyframes spin {
+   from { .rotate(0deg); }
+   to { .rotate(360deg); }
+ }
index 0000000000000000000000000000000000000000,fe6fc385a58e1168c0edca8c7fff813585d7a050..586c37610df65ac8e405ab42933a43300bb5f4d1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,84 +1,90 @@@
 -<div class="coding-rules-detail-quality-profile-name">{{name}}</div>
 -
 -{{#if parent}}
 -  <div class="coding-rules-detail-quality-profile-inheritance">
 -    <i class="icon-inheritance"></i> {{t 'coding_rules.inherits'}} <strong>{{parent.name}}</strong>
 -  </div>
 -{{/if}}
++<div class="coding-rules-detail-quality-profile-name">
++  {{name}}
++  {{#if parent}}
++    <span class="coding-rules-detail-quality-profile-inheritance">
++        <i class="icon-inheritance"></i> {{parent.name}}
++      </span>
++  {{/if}}
++</div>
+ <ul class="coding-rules-detail-parameters coding-rules-detail-quality-profile-parameters">
+   <li class="coding-rules-detail-parameter">
+     <h3 class="coding-rules-detail-parameter-name">{{t 'severity'}}</h3>
+     <div class="coding-rules-detail-parameter-description">
+       <select class="coding-rules-detail-quality-profile-severity">
+         {{#each severities}}
+           <option value="{{this}}">{{t 'severity' this}}</option>
+         {{/each}}
+       </select>
+       {{#if parent}}
+         {{#notEq severity parent.severity}}
 -          {{t 'coding_rules.original'}} {{severityIcon parent.severity}}{{t 'severity' parent.severity}}
++          <span class="coding-rules-detail-parameter-original">
++              {{t 'coding_rules.original'}} {{severityIcon parent.severity}}{{t 'severity' parent.severity}}
++            </span>
+         {{/notEq}}
+       {{/if}}
+     </div>
+   </li>
+   {{#each parameters}}
+     <li class="coding-rules-detail-parameter">
+       <h3 class="coding-rules-detail-parameter-name">{{key}}</h3>
+       <div class="coding-rules-detail-parameter-description">
+         <input type="text" value="{{value}}">
+         {{#if ../parent}}
+           {{#notEq value original}}
 -            {{t 'coding_rules.original'}} {{original}}
++            <span class="coding-rules-detail-parameter-original">
++                {{t 'coding_rules.original'}} {{original}}
++              </span>
+           {{/notEq}}
+         {{/if}}
+       </div>
+     </li>
+   {{/each}}
+ </ul>
 -<div class="coding-rules-detail-quality-profile-note">
 -  {{#if note}}
++<div class="button-group coding-rules-detail-quality-profile-actions">
++  <button class="coding-rules-detail-quality-profile-update" disabled>{{t 'update'}}</button>
++  {{#unless note}}
++    <button class="coding-rules-detail-quality-profile-note-add">{{t 'coding_rules.add_note'}}</button>
++  {{/unless}}
++  {{#if parent}}
++    <button class="button-red">{{t 'coding_rules.revert_to_parent_definition'}}</button>
++  {{/if}}
++  <button class="button-red">{{t 'coding_rules.deactivate_quality_profile'}}</button>
++</div>
++
++{{#if note}}
++  <div class="coding-rules-detail-quality-profile-note">
+     <blockquote class="rule-desc">
+       <cite>
+         <b>{{note.username}}</b> ({{note.fCreationDate}}) &nbsp;|&nbsp;
+         <a class="coding-rules-detail-quality-profile-note-edit link-action">{{t 'edit'}}</a>&nbsp;
+         <a class="coding-rules-detail-quality-profile-note-delete link-action">{{t 'delete'}}</a>
+       </cite>
+       {{{note.html}}}
+     </blockquote>
 -  {{else}}
 -    <a class="coding-rules-detail-quality-profile-note-add link-action">{{t 'coding_rules.add_note'}}</a>
 -  {{/if}}
 -</div>
++  </div>
++{{/if}}
+ <div class="coding-rules-detail-quality-profile-note-form admin">
+   <table class="width100 table">
+     <tbody>
+     <tr>
+       <td class="width100" colspan="2">
+         <textarea class="coding-rules-detail-quality-profile-note-text" rows="4" style="width: 100%;">{{#if note}}{{note.raw}}{{/if}}</textarea>
+       </td>
+     </tr>
+     <tr>
+       <td>
+         <button class="coding-rules-detail-quality-profile-note-submit">
+           {{#if note}}{{t 'update'}}{{else}}{{t 'coding_rules.add_note'}}{{/if}}
+         </button>
+         <a class="coding-rules-detail-quality-profile-note-cancel action">{{t 'cancel'}}</a>
+       </td>
+       <td class="right">
 -        {{> '_markdown-tips' }}
++        <%= render :partial => 'markdown/tips' -%>
+       </td>
+     </tr>
+     </tbody>
+   </table>
 -</div>
 -
 -<div class="button-group coding-rules-detail-quality-profile-actions">
 -  <button>{{t 'update'}}</button>
 -  {{#if parent}}
 -    <button class="button-red">{{t 'coding_rules.revert_to_parent_definition'}}</button>
 -  {{/if}}
 -  <button class="button-red">{{t 'coding_rules.deactivate_quality_profile'}}</button>
+ </div>
index 0000000000000000000000000000000000000000,50e3e04484a3a2513c358956a4beccfe58c0b12c..572bdb2af799bde01a418afb75b0b72d30258be1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,57 +1,59 @@@
 -    <input name="warning" class="measure-input" data-type="{{metric.type}}" type="text">
+ <td nowrap>
+   {{metric.name}}
+ </td>
+ <td width="10%" nowrap>
+   {{#if canEdit}}
+     <select name="period">
+       {{#unless isDiffMetric}}<option value="0">{{t 'value'}}</option>{{/unless}}
+       {{#each periods}}<option value="{{key}}">&Delta; {{text}}</option>{{/each}}
+     </select>
+   {{else}}
+     {{#if periodText}}&Delta; {{periodText}}
+     {{else}}{{t 'value'}}
+     {{/if}}
+   {{/if}}
+ </td>
+ <td width="10%" nowrap>
+   {{#if canEdit}}
+     <select name="operator">
+       {{#operators metric.type}}
+         <option value="{{this}}">{{t 'quality_gates.operator' this}}</option>
+       {{/operators}}
+     </select>
+   {{else}}
+     {{t 'quality_gates.operator' op}}
+   {{/if}}
+ </td>
+ <td width="15%" nowrap="nowrap">
+   <i class="icon-alert-warn" title="{{t 'alerts.warning_tooltip'}}"></i>
+   {{#if canEdit}}
 -    <input name="error" class="measure-input" data-type="{{metric.type}}" type="text">
++    <input name="warning" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
++           type="text">
+   {{else}}
+     {{warning}}
+   {{/if}}
+ </td>
+ <td width="15%" nowrap="nowrap">
+   <i class="icon-alert-error" title="{{t 'alerts.error_tooltip'}}"></i>
+   {{#if canEdit}}
++    <input name="error" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
++           type="text">
+   {{else}}
+     {{error}}
+   {{/if}}
+ </td>
+ <td class="quality-gate-condition-actions" width="120px" nowrap>
+   {{#if canEdit}}
+     {{#if id}}
+       <div class="button-group">
+         <button class="update-condition" disabled>{{t 'update_verb'}}</button>
+         <button class="button-red delete-condition">{{t 'delete'}}</button>
+       </div>
+     {{else}}
+       <div class="button-group">
+         <button class="add-condition">{{t 'add_verb'}}</button>
+         <a class="action cancel-add-condition">{{t 'cancel'}}</a>
+       </div>
+     {{/if}}
+   {{/if}}
+ </td>