]> source.dussan.org Git - sonarqube.git/commitdiff
migrate some code from coffee to js
authorStas Vilchik <vilchiks@gmail.com>
Tue, 26 May 2015 12:28:13 +0000 (14:28 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Tue, 26 May 2015 12:29:44 +0000 (14:29 +0200)
46 files changed:
server/sonar-web/Gruntfile.coffee
server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee [deleted file]
server/sonar-web/src/main/coffee/components/common/dialogs.coffee [deleted file]
server/sonar-web/src/main/coffee/components/common/inputs.coffee [deleted file]
server/sonar-web/src/main/coffee/components/common/popup.coffee [deleted file]
server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee [deleted file]
server/sonar-web/src/main/js/apps/computation/app.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/header-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/layout.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/list-footer-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/list-item-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/list-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/report.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/reports.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/router.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/search-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
server/sonar-web/src/main/js/components/common/dialogs.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/common/popup.js [new file with mode: 0644]
server/sonar-web/src/main/js/libs/inputs.js [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb [new file with mode: 0644]
server/sonar-web/src/test/js/computation-spec.js [new file with mode: 0644]
server/sonar-web/src/test/json/computation-spec/history-big-1.json [new file with mode: 0644]
server/sonar-web/src/test/json/computation-spec/history-big-2.json [new file with mode: 0644]
server/sonar-web/src/test/json/computation-spec/history.json [new file with mode: 0644]
server/sonar-web/src/test/json/computation-spec/queue.json [new file with mode: 0644]
server/sonar-web/src/test/views/computation.jade [new file with mode: 0644]
server/sonar-web/src/test/views/layouts/main.jade

index 48d06ebbac46ffbede0778b9b2df96f76b579083..ac2a0b1bbf7d3fa38aec64dc27c73c64eefe2a12 100644 (file)
@@ -87,7 +87,7 @@ module.exports = (grunt) ->
             '<%= BUILD_PATH %>/js/libs/graphics/barchart.js'
             '<%= BUILD_PATH %>/js/libs/sortable.js'
 
-            '<%= BUILD_PATH %>/js/components/common/inputs.js'
+            '<%= BUILD_PATH %>/js/libs/inputs.js'
             '<%= BUILD_PATH %>/js/components/common/dialogs.js'
             '<%= BUILD_PATH %>/js/components/common/processes.js'
             '<%= BUILD_PATH %>/js/components/common/jquery-isolated-scroll.js'
@@ -132,9 +132,9 @@ module.exports = (grunt) ->
         tasks: [
           { grunt: true, args: ['uglify:build'] }
           # apps
-          { grunt: true, args: ['requirejs:app', '--app=analysis-reports'] }
           { grunt: true, args: ['requirejs:app', '--app=api-documentation'] }
           { grunt: true, args: ['requirejs:app', '--app=coding-rules'] }
+          { grunt: true, args: ['requirejs:app', '--app=computation'] }
           { grunt: true, args: ['requirejs:app', '--app=drilldown'] }
           { grunt: true, args: ['requirejs:app', '--app=markdown'] }
           { grunt: true, args: ['requirejs:app', '--app=measures'] }
@@ -171,6 +171,7 @@ module.exports = (grunt) ->
           'casper:workspace'
           'casper:users'
           'casper:provisioning'
+          'casper:computation'
         ]
 
 
@@ -213,9 +214,6 @@ module.exports = (grunt) ->
           '<%= BUILD_PATH %>/js/apps/api-documentation/templates.js': [
             '<%= SOURCE_PATH %>/js/apps/api-documentation/templates/**/*.hbs'
           ]
-          '<%= BUILD_PATH %>/js/apps/analysis-reports/templates.js': [
-            '<%= SOURCE_PATH %>/coffee/apps/analysis-reports/templates/**/*.hbs'
-          ]
           '<%= BUILD_PATH %>/js/apps/nav/templates.js': [
             '<%= SOURCE_PATH %>/js/apps/nav/templates/**/*.hbs'
           ]
@@ -234,6 +232,9 @@ module.exports = (grunt) ->
           '<%= BUILD_PATH %>/js/apps/provisioning/templates.js': [
             '<%= SOURCE_PATH %>/js/apps/provisioning/templates/**/*.hbs'
           ]
+          '<%= BUILD_PATH %>/js/apps/computation/templates.js': [
+            '<%= SOURCE_PATH %>/js/apps/computation/templates/**/*.hbs'
+          ]
 
 
     clean:
@@ -330,6 +331,8 @@ module.exports = (grunt) ->
         src: ['src/test/js/users*.js']
       provisioning:
         src: ['src/test/js/provisioning*.js']
+      computation:
+        src: ['src/test/js/computation*.js']
 
     uglify:
       build:
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/app.coffee
deleted file mode 100644 (file)
index 0d7c20d..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-define [
-  './router'
-  './layout'
-  './models/reports'
-  './views/reports-view'
-  './views/actions-view'
-], (
-  Router
-  Layout
-  Reports
-  ReportsView
-  ActionsView
-) ->
-
-  # Add html class to mark the page as navigator page
-  jQuery('html').addClass 'navigator-page'
-
-
-  # Create an Application
-  App = new Marionette.Application
-
-
-  App.fetchReports = ->
-    fetch = if @state.get 'active' then @reports.fetchActive() else @reports.fetchHistory()
-    @layout.showSpinner 'actionsRegion'
-    @layout.resultsRegion.reset()
-    fetch.done =>
-      @state.set page: @reports.paging.page
-      @reportsView = new ReportsView
-        app: @
-        collection: @reports
-      @layout.resultsRegion.show @reportsView
-
-      unless @state.get('active') || @reports.paging.maxResultsReached
-        @reportsView.bindScrollEvents() unless @state.get 'active'
-
-      @actionsView = new ActionsView
-        app: @
-        collection: @reports
-      @layout.actionsRegion.show @actionsView
-
-
-  App.fetchNextPage = ->
-    @reports.fetchHistory
-      data:
-        p: @state.get('page') + 1
-      remove: false
-    .done =>
-      @state.set page: @reports.paging.page
-
-
-  App.addInitializer ->
-    @state = new Backbone.Model()
-    @state.on 'change:active', => @fetchReports()
-
-
-  App.addInitializer ->
-    @layout = new Layout app: @
-    jQuery('#analysis-reports').empty().append @layout.render().el
-
-
-  App.addInitializer ->
-    @reports = new Reports()
-    @router = new Router app: @
-    Backbone.history.start()
-
-
-  l10nXHR = window.requestMessages()
-  jQuery.when(l10nXHR).done -> App.start()
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/layout.coffee
deleted file mode 100644 (file)
index 5aa028e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  './templates'
-], ->
-
-  class extends Marionette.Layout
-    template: Templates['analysis-reports-layout']
-
-
-
-    regions:
-      actionsRegion: '.analysis-reports-actions'
-      resultsRegion: '.analysis-reports-results'
-
-
-    showSpinner: (region) ->
-      @[region].show new Marionette.ItemView
-        template: _.template('<i class="spinner"></i>')
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/report.coffee
deleted file mode 100644 (file)
index 28700ed..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define ->
-
-
-  class extends Backbone.Model
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/models/reports.coffee
deleted file mode 100644 (file)
index 1689eed..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  './report'
-], (
-  Report
-) ->
-
-
-  class extends Backbone.Collection
-    model: Report
-
-
-    parse: (r) ->
-      @paging =
-        page: r.p
-        pageSize: r.ps
-        total: r.total
-        maxResultsReached: r.p * r.ps >= r.total
-      r.reports
-
-
-    fetchActive: ->
-      @fetch { url: "#{baseUrl}/api/computation/queue" }, { reset: true }
-
-
-    fetchHistory: (options = {  }) ->
-      _.extend options,
-        url: "#{baseUrl}/api/computation/history"
-      options.data = options.data || {}
-      options.data.ps = 50
-      @fetch options, { reset: true }
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/router.coffee
deleted file mode 100644 (file)
index 1bebf0e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define ->
-
-  class AppRouter extends Backbone.Router
-
-    routes:
-      '': 'showCurrent'
-      'current': 'showCurrent'
-      'past': 'showPast'
-
-
-    initialize: (options) ->
-      @options = options
-
-
-    showCurrent: ->
-      @navigate 'current'
-      @options.app.state.set active: true
-
-
-    showPast: ->
-      @navigate 'past'
-      @options.app.state.set active: false
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-actions.hbs
deleted file mode 100644 (file)
index 90f24e6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<ul class="tabs">
-  <li><a class="js-show-current-activity {{#if state.active}}selected{{/if}}">{{t 'analysis_reports.current_activity'}}</a></li>
-  <li><a class="js-show-past-reports {{#unless state.active}}selected{{/unless}}">{{t 'analysis_reports.past_reports'}}</a></li>
-</ul>
-
-<div class="analysis-reports-total">{{tp 'analysis_reports.x_reports' total}}</div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-empty.hbs
deleted file mode 100644 (file)
index b044d70..0000000
+++ /dev/null
@@ -1 +0,0 @@
-No reports to show
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-layout.hbs
deleted file mode 100644 (file)
index e00c677..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<header class="page-header">
-  <h1 class="page-title">{{t 'analysis_reports.page'}}</h1>
-  <p class="page-description">
-    The server is in charge to process reports submitted by batch analyses. This page allows to monitor the queue of
-    pending reports to process, and gives access to the history of past analyses.
-  </p>
-</header>
-
-<div class="analysis-reports-actions"></div>
-<div class="analysis-reports-results"></div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs b/server/sonar-web/src/main/coffee/apps/analysis-reports/templates/analysis-reports-report.hbs
deleted file mode 100644 (file)
index b816c2f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<div class="line">
-  <span class="analysis-reports-project">
-    <i class="icon-qualifier-trk"></i>
-    <a href="{{dashboardUrl projectKey}}">{{projectName}}</a>
-  </span>
-
-  <span class="analysis-reports-timestamp line-small">
-    {{#if submittedAt}}Submitted: {{dt submittedAt}}<br>{{/if}}
-    {{#if startedAt}}Started: {{dt startedAt}}<br>{{/if}}
-    {{#if finishedAt}}Finished: {{dt finishedAt}}<br>{{/if}}
-  </span>
-
-  {{#if duration}}
-    <span class="analysis-reports-timestamp line-small">
-      Duration:
-      {{#gt duration.hours 0}}{{duration.hours}}h{{/gt}}
-      {{#gt duration.minutes 0}}{{duration.minutes}}m{{/gt}}
-      {{duration.seconds}}s
-    </span>
-  {{/if}}
-
-</div>
-
-<div class="analysis-reports-report-id">{{key}} {{status}}</div>
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/actions-view.coffee
deleted file mode 100644 (file)
index 89ae904..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  '../templates'
-], ->
-
-  class extends Marionette.ItemView
-    template: Templates['analysis-reports-actions']
-
-
-    events:
-      'click .js-show-past-reports': 'showPastReports'
-      'click .js-show-current-activity': 'showCurrentActivity'
-
-
-    initialize: (options) ->
-      @listenTo options.collection, 'all', @render
-      @listenTo options.app.state, 'change', @render
-
-
-    showPastReports: ->
-      @options.app.router.navigate 'past', trigger: true
-
-
-    showCurrentActivity: ->
-      @options.app.router.navigate 'current', trigger: true
-
-
-    serializeData: ->
-      _.extend super,
-        state: @options.app.state.toJSON()
-        total: @collection.paging.total || @collection.length
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/report-view.coffee
deleted file mode 100644 (file)
index 4af3d06..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  '../templates'
-], ->
-
-  class extends Marionette.ItemView
-    tagName: 'li'
-    template: Templates['analysis-reports-report']
-
-
-    onRender: ->
-      status = @model.get 'status'
-      @$el.addClass 'analysis-reports-report-pending' if status is 'PENDING'
-      @$el.addClass 'analysis-reports-report-working' if status is 'WORKING'
-      @$el.addClass 'analysis-reports-report-done' if status is 'SUCCESS'
-      @$el.addClass 'analysis-reports-report-cancelled' if status is 'CANCELLED'
-      @$el.addClass 'analysis-reports-report-failed' if status is 'FAIL'
-
-
-    serializeData: ->
-      duration = null
-      if @model.has 'startedAt'
-        startedAtMoment = moment @model.get 'startedAt'
-        finishedAtMoment = moment(@model.get('finishedAt') || new Date())
-        duration = finishedAtMoment.diff startedAtMoment
-        duration =
-          seconds: Math.floor (duration / 1000) % 60
-          minutes: Math.floor (duration / (1000 * 60)) % 60
-          hours: Math.floor (duration / (1000 * 60 * 60)) % 24
-      _.extend super,
-        duration: duration
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-empty-view.coffee
deleted file mode 100644 (file)
index c5c16ea..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  '../templates'
-], ->
-
-  class extends Marionette.ItemView
-    className: 'analysis-reports-no-results'
-    tagName: 'li'
-    template: Templates['analysis-reports-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee b/server/sonar-web/src/main/coffee/apps/analysis-reports/views/reports-view.coffee
deleted file mode 100644 (file)
index 32efc6f..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  './report-view'
-  './reports-empty-view'
-], (
-  ReportView
-  EmptyView
-) ->
-
-  $ = jQuery
-
-
-  class extends Marionette.CollectionView
-    tagName: 'ol'
-    className: 'navigator-results-list'
-    itemView: ReportView
-    emptyView: EmptyView
-
-
-    itemViewOptions: ->
-      listView: @, app: @options.app
-
-
-    initialize: ->
-      @loadMoreThrottled = _.throttle @loadMore, 200
-
-
-    onClose: ->
-      @unbindScrollEvents()
-
-
-    bindScrollEvents: ->
-      $(window).on 'scroll', (=> @loadMoreThrottled())
-
-
-    unbindScrollEvents: ->
-      $(window).off 'scroll'
-
-
-    loadMore: ->
-      lastItem = this.children.findByIndex(@collection.length - 1)
-      if $(window).scrollTop() + $(window).outerHeight() >= lastItem.$el.offset().top - 40
-        @unbindScrollEvents()
-        @options.app.fetchNextPage().done =>
-          @bindScrollEvents() unless @collection.paging.maxResultsReached
diff --git a/server/sonar-web/src/main/coffee/components/common/dialogs.coffee b/server/sonar-web/src/main/coffee/components/common/dialogs.coffee
deleted file mode 100644 (file)
index e9dd438..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-$ = jQuery
-
-window.confirmDialog = (options) ->
-  settings = _.extend window.confirmDialog.defaults, options
-  dialog = $ """
-             <div>
-               <div class='modal-head'><h2>#{settings.title}</h2></div>
-               <div class='modal-body'>#{settings.html}</div>
-               <div class='modal-foot'>
-                 <button data-confirm='yes'>#{settings.yesLabel}</button>
-                 <a data-confirm='no' class='action'>#{settings.noLabel}</a>
-               </div>
-             </div>
-             """
-
-  $('[data-confirm=yes]', dialog).on 'click', ->
-    dialog.dialog 'close'
-    settings.yesHandler()
-    settings.always()
-  $('[data-confirm=no]', dialog).on 'click', ->
-    dialog.dialog 'close'
-    settings.noHandler()
-    settings.always()
-
-  dialog.dialog
-    modal: true
-    minHeight: null
-    width: 540
-
-
-window.confirmDialog.defaults =
-  title: 'Confirmation'
-  html: ''
-  yesLabel: 'Yes'
-  noLabel: 'Cancel'
-  yesHandler: ->
-  noHandler: ->
-  always: ->
\ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/components/common/inputs.coffee b/server/sonar-web/src/main/coffee/components/common/inputs.coffee
deleted file mode 100644 (file)
index 70e7ac9..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-$ = jQuery
-
-window.SS = if typeof window.SS == 'object' then window.SS else {}
-window.SS.hoursInDay = window.SS.hoursInDay || 8
-
-transformPattern = (pattern) ->
-  return pattern.replace /\{0\}/g, '(\\d+)'
-
-
-convertWorkDuration = (value) ->
-  return 0 if value == '0'
-
-  daysPattern = transformPattern t('work_duration.x_days')
-  hoursPattern = transformPattern t('work_duration.x_hours')
-  minutesPattern = transformPattern t('work_duration.x_minutes')
-
-  days = value.match daysPattern
-  hours = value.match hoursPattern
-  minutes = value.match minutesPattern
-
-  days = if days then +days[1] else 0
-  hours = if hours then +hours[1] else 0
-  minutes = if minutes then +minutes[1] else 0
-
-  if !value
-    value
-  else
-    (days * window.SS.hoursInDay + hours) * 60 + minutes
-
-
-restoreWorkDuration = (value) ->
-  return '0' if (value == '0' || value == 0)
-  return value unless /^\d+$/.test value
-
-  days = Math.floor(value / (window.SS.hoursInDay * 60))
-  hours = Math.floor((value - days * window.SS.hoursInDay * 60) / 60)
-  minutes = value % 60
-  result = []
-  result.push t('work_duration.x_days').replace('{0}', days) if days > 0
-  result.push t('work_duration.x_hours').replace('{0}', hours) if hours > 0
-  result.push t('work_duration.x_minutes').replace('{0}', minutes) if minutes > 0
-  result.join ' '
-
-
-convertRating = (value) ->
-  if /^[ABCDE]$/.test(value)
-    value.charCodeAt(0) - 'A'.charCodeAt(0) + 1
-  else
-    value
-
-
-convertValue = (value, input) ->
-  type = input.data 'type'
-
-  # No convertation if input doesn't have data-type
-  return value unless type?
-
-  # Do necessary convertion depeneds on input data-type
-  switch type
-    when 'WORK_DUR' then convertWorkDuration value
-    when 'RATING' then convertRating value
-    else value
-
-
-restoreRating = (value) ->
-  return value unless /^[12345]+$/.test value
-  String.fromCharCode(value - 1 + 'A'.charCodeAt(0))
-
-
-restoreValue = (value, input) ->
-  type = input.data 'type'
-
-  # No convertation if input doesn't have data-type
-  return value unless type?
-
-  # Do necessary convertion depeneds on input data-type
-  switch type
-    when 'WORK_DUR' then restoreWorkDuration value
-    when 'RATING' then restoreRating value
-    else value
-
-
-originalVal = $.fn.val
-$.fn.val = (value) ->
-  if arguments.length
-    originalVal.call @, (restoreValue value, @)
-  else
-    convertValue originalVal.call(@), @
-
-$.fn.originalVal = originalVal
diff --git a/server/sonar-web/src/main/coffee/components/common/popup.coffee b/server/sonar-web/src/main/coffee/components/common/popup.coffee
deleted file mode 100644 (file)
index 52f2804..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define ->
-
-  $ = jQuery
-
-
-  class PopupView extends Marionette.ItemView
-    className: 'bubble-popup'
-
-
-    onRender: ->
-      @$el.detach().appendTo $('body')
-
-      if @options.bottom
-        @$el.addClass 'bubble-popup-bottom'
-        @$el.css
-          top: @options.triggerEl.offset().top + @options.triggerEl.outerHeight()
-          left: @options.triggerEl.offset().left
-      else if @options.bottomRight
-        @$el.addClass 'bubble-popup-bottom-right'
-        @$el.css
-          top: @options.triggerEl.offset().top + @options.triggerEl.outerHeight()
-          right: $(window).width() - @options.triggerEl.offset().left - @options.triggerEl.outerWidth()
-      else
-        @$el.css
-          top: @options.triggerEl.offset().top
-          left: @options.triggerEl.offset().left + @options.triggerEl.outerWidth()
-
-      @attachCloseEvents()
-
-
-    attachCloseEvents: ->
-      key 'escape', => @close()
-
-      $('body').on 'click.bubble-popup', =>
-        $('body').off 'click.bubble-popup'
-        @close()
-
-      @options.triggerEl.on 'click.bubble-popup', (e) =>
-        @options.triggerEl.off 'click.bubble-popup'
-        e.stopPropagation()
-        @close()
-
-
-    onClose: ->
-      $('body').off 'click.bubble-popup'
-      @options.triggerEl.off 'click.bubble-popup'
diff --git a/server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee b/server/sonar-web/src/main/coffee/components/navigator/filters/date-filter-view.coffee
deleted file mode 100644 (file)
index afc0b4f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-
-define [
-  './string-filters'
-], (
-  StringFilterView
-) ->
-
-  class DateFilterView extends StringFilterView
-
-    render: ->
-      super
-      @detailsView.$('input').prop('placeholder', '1970-01-31').datepicker
-        dateFormat: 'yy-mm-dd'
-        changeMonth: true
-        changeYear: true
diff --git a/server/sonar-web/src/main/js/apps/computation/app.js b/server/sonar-web/src/main/js/apps/computation/app.js
new file mode 100644 (file)
index 0000000..47b483f
--- /dev/null
@@ -0,0 +1,64 @@
+define([
+  './router',
+  './layout',
+  './reports',
+  './header-view',
+  './search-view',
+  './list-view',
+  './list-footer-view'
+], function (Router, Layout, Reports, HeaderView, SearchView, ListView, ListFooterView) {
+
+  var App = new Marionette.Application(),
+      init = function (options) {
+        // Collection
+        this.reports = new Reports();
+
+        // Router
+        this.router = new Router({ reports: this.reports });
+
+        // Layout
+        this.layout = new Layout({ el: options.el });
+        this.layout.render();
+
+        // Header View
+        this.headerView = new HeaderView({ collection: this.reports });
+        this.layout.headerRegion.show(this.headerView);
+
+        // Search View
+        this.searchView = new SearchView({
+          collection: this.reports,
+          router: this.router
+        });
+        this.layout.searchRegion.show(this.searchView);
+
+        // List View
+        this.listView = new ListView({ collection: this.reports });
+        this.layout.listRegion.show(this.listView);
+
+        // List Footer View
+        this.listFooterView = new ListFooterView({ collection: this.reports });
+        this.layout.listFooterRegion.show(this.listFooterView);
+
+        // Go!
+        Backbone.history.start({
+          pushState: true,
+          root: getRoot()
+        });
+      };
+
+  App.on('start', function (options) {
+    window.requestMessages().done(function () {
+      init.call(App, options);
+    });
+  });
+
+  function getRoot () {
+    var COMPUTATION = '/computation',
+        path = window.location.pathname,
+        pos = path.indexOf(COMPUTATION);
+    return path.substr(0, pos + COMPUTATION.length);
+  }
+
+  return App;
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/header-view.js b/server/sonar-web/src/main/js/apps/computation/header-view.js
new file mode 100644 (file)
index 0000000..8e010c0
--- /dev/null
@@ -0,0 +1,9 @@
+define([
+  './templates'
+], function () {
+
+  return Marionette.ItemView.extend({
+    template: Templates['computation-header']
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/layout.js b/server/sonar-web/src/main/js/apps/computation/layout.js
new file mode 100644 (file)
index 0000000..f747efe
--- /dev/null
@@ -0,0 +1,16 @@
+define([
+  './templates'
+], function () {
+
+  return Marionette.Layout.extend({
+    template: Templates['computation-layout'],
+
+    regions: {
+      headerRegion: '#computation-header',
+      searchRegion: '#computation-search',
+      listRegion: '#computation-list',
+      listFooterRegion: '#computation-list-footer'
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-footer-view.js b/server/sonar-web/src/main/js/apps/computation/list-footer-view.js
new file mode 100644 (file)
index 0000000..5034f25
--- /dev/null
@@ -0,0 +1,34 @@
+define([
+  './templates'
+], function () {
+
+  return Marionette.ItemView.extend({
+    template: Templates['computation-list-footer'],
+
+    collectionEvents: {
+      'all': 'render'
+    },
+
+    events: {
+      'click #computation-fetch-more': 'onMoreClick'
+    },
+
+    onMoreClick: function (e) {
+      e.preventDefault();
+      this.fetchMore();
+    },
+
+    fetchMore: function () {
+      this.collection.fetchMore();
+    },
+
+    serializeData: function () {
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        total: this.collection.total,
+        count: this.collection.length,
+        more: this.collection.hasMore()
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-item-view.js b/server/sonar-web/src/main/js/apps/computation/list-item-view.js
new file mode 100644 (file)
index 0000000..5545f2b
--- /dev/null
@@ -0,0 +1,28 @@
+define([
+  './templates'
+], function () {
+
+  return Marionette.ItemView.extend({
+    tagName: 'li',
+    className: 'panel panel-vertical',
+    template: Templates['computation-list-item'],
+
+    onRender: function () {
+      this.$el.attr('data-id', this.model.id);
+      this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
+    },
+
+    onClose: function () {
+      this.$('[data-toggle="tooltip"]').tooltip('destroy');
+    },
+
+    serializeData: function () {
+      var dangerStatuses = ['CANCELLED', 'FAIL'];
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        duration: this.model.getDuration(),
+        danger: dangerStatuses.indexOf(this.model.get('status')) !== -1
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/list-view.js b/server/sonar-web/src/main/js/apps/computation/list-view.js
new file mode 100644 (file)
index 0000000..138c36b
--- /dev/null
@@ -0,0 +1,11 @@
+define([
+  './list-item-view',
+  './templates'
+], function (ListItemView) {
+
+  return Marionette.CollectionView.extend({
+    tagName: 'ul',
+    itemView: ListItemView
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/report.js b/server/sonar-web/src/main/js/apps/computation/report.js
new file mode 100644 (file)
index 0000000..1131c24
--- /dev/null
@@ -0,0 +1,22 @@
+define(function () {
+
+  return Backbone.Model.extend({
+    idAttribute: 'key',
+
+    getDuration: function () {
+      var duration = null;
+      if (this.has('startedAt')) {
+        var startedAtMoment = moment(this.get('startedAt')),
+            finishedAtMoment = moment(this.get('finishedAt') || new Date()),
+            diff = finishedAtMoment.diff(startedAtMoment);
+        duration = {
+          seconds: Math.floor(diff / 1000) % 60,
+          minutes: Math.floor(diff / (1000 * 60)) % 60,
+          hours: Math.floor(diff / (1000 * 60 * 60)) % 24
+        };
+      }
+      return duration;
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/reports.js b/server/sonar-web/src/main/js/apps/computation/reports.js
new file mode 100644 (file)
index 0000000..9c81443
--- /dev/null
@@ -0,0 +1,34 @@
+define([
+  './report'
+], function (Report) {
+
+  return Backbone.Collection.extend({
+    model: Report,
+    url: '',
+
+    parse: function (r) {
+      this.total = r.total || r.reports.length;
+      this.p = r.p || 1;
+      this.ps = r.ps;
+      return r.reports;
+    },
+
+    fetch: function (options) {
+      var opts = _.defaults(options || {}, { q: this.q }, { q: 'history' });
+      opts.url = baseUrl + '/api/computation/' + opts.q;
+      this.q = opts.q;
+      return Backbone.Collection.prototype.fetch.call(this, opts);
+    },
+
+    fetchMore: function () {
+      var p = this.p + 1;
+      return this.fetch({ add: true, remove: false, data: { p: p, ps: this.ps } });
+    },
+
+    hasMore: function () {
+      return this.total > this.p * this.ps;
+    }
+
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/router.js b/server/sonar-web/src/main/js/apps/computation/router.js
new file mode 100644 (file)
index 0000000..6d87480
--- /dev/null
@@ -0,0 +1,29 @@
+define(function () {
+
+  return Backbone.Router.extend({
+    routes: {
+      '': 'index',
+      'index': 'index',
+      'current': 'current',
+      'past': 'past'
+    },
+
+    initialize: function (options) {
+      this.options = options;
+    },
+
+    index: function () {
+      this.navigate('current');
+      this.current();
+    },
+
+    current: function () {
+      this.options.reports.fetch({ q: 'queue' });
+    },
+
+    past: function () {
+      this.options.reports.fetch({ q: 'history' });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/search-view.js b/server/sonar-web/src/main/js/apps/computation/search-view.js
new file mode 100644 (file)
index 0000000..75ab65c
--- /dev/null
@@ -0,0 +1,34 @@
+define([
+  './templates'
+], function () {
+
+  return Marionette.ItemView.extend({
+    template: Templates['computation-search'],
+
+    collectionEvents: {
+      'all': 'render'
+    },
+
+    events: {
+      'click .js-queue': 'queue',
+      'click .js-history': 'history'
+    },
+
+    queue: function (e) {
+      e.preventDefault();
+      this.options.router.navigate('current', { trigger: true });
+    },
+
+    history: function (e) {
+      e.preventDefault();
+      this.options.router.navigate('past', { trigger: true });
+    },
+
+    serializeData: function () {
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        tab: this.collection.q
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-header.hbs
new file mode 100644 (file)
index 0000000..1995afd
--- /dev/null
@@ -0,0 +1,5 @@
+<header class="page-header">
+  <h1 class="page-title">Project Computation</h1>
+  <p class="page-description">The server is in charge to process reports submitted by batch analyses. This page allows
+    to monitor the queue of pending reports to process, and gives access to the history of past analyses.</p>
+</header>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-layout.hbs
new file mode 100644 (file)
index 0000000..0d6d7f0
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="page">
+  <div id="computation-header"></div>
+  <div id="computation-search"></div>
+  <div id="computation-list"></div>
+  <div id="computation-list-footer"></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-footer.hbs
new file mode 100644 (file)
index 0000000..5a61264
--- /dev/null
@@ -0,0 +1,6 @@
+<footer class="spacer-top note text-center">
+  {{count}}/{{total}} shown
+  {{#if more}}
+    <a id="computation-fetch-more" class="spacer-left" href="#">show more</a>
+  {{/if}}
+</footer>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-list-item.hbs
new file mode 100644 (file)
index 0000000..ab30221
--- /dev/null
@@ -0,0 +1,32 @@
+<div class="display-inline-block text-top width-30">
+  <i class="icon-qualifier-trk"></i>
+  <a href="{{dashboardUrl projectKey}}">{{projectName}}</a>
+</div>
+
+<div class="display-inline-block text-top width-30">
+  <ul>
+    {{#if submittedAt}}
+      <li>Submitted: {{dt submittedAt}}</li>
+    {{/if}}
+    {{#if startedAt}}
+      <li>Started: {{dt startedAt}}</li>
+    {{/if}}
+    {{#if finishedAt}}
+      <li>Finished: {{dt finishedAt}}</li>
+    {{/if}}
+  </ul>
+</div>
+
+
+{{#if duration}}
+  <div class="display-inline-block text-top width-30">
+    Duration:
+    {{#gt duration.hours 0}}{{duration.hours}}h{{/gt}}
+    {{#gt duration.minutes 0}}{{duration.minutes}}m{{/gt}}
+    {{duration.seconds}}s
+  </div>
+{{/if}}
+
+<div class="pull-right {{#if danger}}text-danger{{/if}}">
+  #{{key}} {{status}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs b/server/sonar-web/src/main/js/apps/computation/templates/computation-search.hbs
new file mode 100644 (file)
index 0000000..63bd417
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="spacer-top">
+  <ul class="tabs">
+    <li><a class="js-queue {{#eq tab 'queue'}}selected{{/eq}}">{{t 'analysis_reports.current_activity'}}</a></li>
+    <li><a class="js-history {{#eq tab 'history'}}selected{{/eq}}">{{t 'analysis_reports.past_reports'}}</a></li>
+  </ul>
+</div>
index 5cc7dbbc1ed053c6694ca079005cdb725f62f753..7ecdc88a5f437a80c5a9716db5da0dc669b4a8fc 100644 (file)
@@ -62,7 +62,7 @@
           <a href="{{link '/bulk_deletion'}}">{{t 'bulk_deletion.page'}}</a>
         </li>
         <li>
-          <a href="{{link '/analysis_reports'}}">{{t 'analysis_reports.page'}}</a>
+          <a href="{{link '/computation'}}">{{t 'analysis_reports.page'}}</a>
         </li>
       </ul>
     </li>
diff --git a/server/sonar-web/src/main/js/components/common/dialogs.js b/server/sonar-web/src/main/js/components/common/dialogs.js
new file mode 100644 (file)
index 0000000..46f8b68
--- /dev/null
@@ -0,0 +1,44 @@
+(function ($) {
+
+  window.confirmDialog = function (options) {
+    var settings = _.extend(window.confirmDialog.defaults, options),
+        dialog = $('<div><div class="modal-head"><h2>' + settings.title + '</h2></div><div class="modal-body">' +
+        settings.html + '</div><div class="modal-foot"><button data-confirm="yes">' + settings.yesLabel +
+        '</button> <a data-confirm="no" class="action">' + settings.noLabel + '</a></div></div>');
+
+    $('[data-confirm=yes]', dialog).on('click', function () {
+      dialog.dialog('close');
+      settings.yesHandler();
+      return settings.always();
+    });
+
+    $('[data-confirm=no]', dialog).on('click', function () {
+      dialog.dialog('close');
+      settings.noHandler();
+      return settings.always();
+    });
+
+    return dialog.dialog({
+      modal: true,
+      minHeight: null,
+      width: 540
+    });
+  };
+
+  window.confirmDialog.defaults = {
+    title: 'Confirmation',
+    html: '',
+    yesLabel: 'Yes',
+    noLabel: 'Cancel',
+    yesHandler: function () {
+      // no op
+    },
+    noHandler: function () {
+      // no op
+    },
+    always: function () {
+      // no op
+    }
+  };
+
+})(window.jQuery);
diff --git a/server/sonar-web/src/main/js/components/common/popup.js b/server/sonar-web/src/main/js/components/common/popup.js
new file mode 100644 (file)
index 0000000..f978b7a
--- /dev/null
@@ -0,0 +1,53 @@
+define(function () {
+
+  var $ = jQuery;
+
+  return Marionette.ItemView.extend({
+    className: 'bubble-popup',
+
+    onRender: function () {
+      this.$el.detach().appendTo($('body'));
+      if (this.options.bottom) {
+        this.$el.addClass('bubble-popup-bottom');
+        this.$el.css({
+          top: this.options.triggerEl.offset().top + this.options.triggerEl.outerHeight(),
+          left: this.options.triggerEl.offset().left
+        });
+      } else if (this.options.bottomRight) {
+        this.$el.addClass('bubble-popup-bottom-right');
+        this.$el.css({
+          top: this.options.triggerEl.offset().top + this.options.triggerEl.outerHeight(),
+          right: $(window).width() - this.options.triggerEl.offset().left - this.options.triggerEl.outerWidth()
+        });
+      } else {
+        this.$el.css({
+          top: this.options.triggerEl.offset().top,
+          left: this.options.triggerEl.offset().left + this.options.triggerEl.outerWidth()
+        });
+      }
+      this.attachCloseEvents();
+    },
+
+    attachCloseEvents: function () {
+      var that = this;
+      key('escape', function () {
+        that.close();
+      });
+      $('body').on('click.bubble-popup', function () {
+        $('body').off('click.bubble-popup');
+        that.close();
+      });
+      this.options.triggerEl.on('click.bubble-popup', function (e) {
+        that.options.triggerEl.off('click.bubble-popup');
+        e.stopPropagation();
+        that.close();
+      });
+    },
+
+    onClose: function () {
+      $('body').off('click.bubble-popup');
+      this.options.triggerEl.off('click.bubble-popup');
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/libs/inputs.js b/server/sonar-web/src/main/js/libs/inputs.js
new file mode 100644 (file)
index 0000000..78673a5
--- /dev/null
@@ -0,0 +1,111 @@
+(function ($) {
+
+  function transformPattern (pattern) {
+    return pattern.replace(/\{0\}/g, '(\\d+)');
+  }
+
+  function convertWorkDuration (value) {
+    var days, daysPattern, hours, hoursPattern, minutes, minutesPattern;
+    if (value === '0') {
+      return 0;
+    }
+    daysPattern = transformPattern(t('work_duration.x_days'));
+    hoursPattern = transformPattern(t('work_duration.x_hours'));
+    minutesPattern = transformPattern(t('work_duration.x_minutes'));
+    days = value.match(daysPattern);
+    hours = value.match(hoursPattern);
+    minutes = value.match(minutesPattern);
+    days = days ? +days[1] : 0;
+    hours = hours ? +hours[1] : 0;
+    minutes = minutes ? +minutes[1] : 0;
+    if (!value) {
+      return value;
+    } else {
+      return (days * window.SS.hoursInDay + hours) * 60 + minutes;
+    }
+  }
+
+  function restoreWorkDuration (value) {
+    var days, hours, minutes, result;
+    if (value === '0' || value === 0) {
+      return '0';
+    }
+    if (!/^\d+$/.test(value)) {
+      return value;
+    }
+    days = Math.floor(value / (window.SS.hoursInDay * 60));
+    hours = Math.floor((value - days * window.SS.hoursInDay * 60) / 60);
+    minutes = value % 60;
+    result = [];
+    if (days > 0) {
+      result.push(t('work_duration.x_days').replace('{0}', days));
+    }
+    if (hours > 0) {
+      result.push(t('work_duration.x_hours').replace('{0}', hours));
+    }
+    if (minutes > 0) {
+      result.push(t('work_duration.x_minutes').replace('{0}', minutes));
+    }
+    return result.join(' ');
+  }
+
+  function convertRating (value) {
+    if (/^[ABCDE]$/.test(value)) {
+      return value.charCodeAt(0) - 'A'.charCodeAt(0) + 1;
+    } else {
+      return value;
+    }
+  }
+
+  function convertValue (value, input) {
+    var type;
+    type = input.data('type');
+    if (type == null) {
+      return value;
+    }
+    switch (type) {
+      case 'WORK_DUR':
+        return convertWorkDuration(value);
+      case 'RATING':
+        return convertRating(value);
+      default:
+        return value;
+    }
+  }
+
+  function restoreRating (value) {
+    if (!/^[12345]+$/.test(value)) {
+      return value;
+    }
+    return String.fromCharCode(value - 1 + 'A'.charCodeAt(0));
+  }
+
+  function restoreValue (value, input) {
+    var type;
+    type = input.data('type');
+    if (type == null) {
+      return value;
+    }
+    switch (type) {
+      case 'WORK_DUR':
+        return restoreWorkDuration(value);
+      case 'RATING':
+        return restoreRating(value);
+      default:
+        return value;
+    }
+  }
+
+  var originalVal = $.fn.val;
+
+  $.fn.val = function (value) {
+    if (arguments.length) {
+      return originalVal.call(this, restoreValue(value, this));
+    } else {
+      return convertValue(originalVal.call(this), this);
+    }
+  };
+
+  $.fn.originalVal = originalVal;
+
+})(window.jQuery);
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/computation_controller.rb
new file mode 100644 (file)
index 0000000..5d825bd
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+
+class ComputationController < ApplicationController
+
+  before_filter :admin_required
+
+  SECTION=Navigation::SECTION_CONFIGURATION
+
+  def index
+    render :action => 'index'
+  end
+
+  def current
+    render :action => 'index'
+  end
+
+  def past
+    render :action => 'index'
+  end
+
+end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/computation/index.html.erb
new file mode 100644 (file)
index 0000000..f77eb79
--- /dev/null
@@ -0,0 +1,6 @@
+<div id="computation"></div>
+<script>
+  require(['apps/computation/app'], function (App) {
+    App.start({ el: '#computation' });
+  });
+</script>
diff --git a/server/sonar-web/src/test/js/computation-spec.js b/server/sonar-web/src/test/js/computation-spec.js
new file mode 100644 (file)
index 0000000..0922feb
--- /dev/null
@@ -0,0 +1,103 @@
+/* globals casper: false */
+var lib = require('../lib'),
+    testName = lib.testName('Computation');
+
+lib.initMessages();
+lib.changeWorkingDirectory('computation-spec');
+lib.configureCasper();
+
+casper.test.begin(testName('List'), 8, function (test) {
+  casper
+      .start(lib.buildUrl('computation'), function () {
+        lib.setDefaultViewport();
+        lib.mockRequestFromFile('/api/computation/queue', 'queue.json');
+        lib.mockRequestFromFile('/api/computation/history', 'history.json');
+      })
+
+      .then(function () {
+        casper.evaluate(function () {
+          require(['apps/computation/app'], function (App) {
+            App.start({ el: '#computation' });
+          });
+        });
+      })
+
+      .then(function () {
+        casper.waitForSelector('#computation-list li');
+      })
+
+      .then(function () {
+        test.assertElementCount('#computation-list li[data-id]', 1);
+        test.assertSelectorContains('#computation-list', 'SonarQube');
+        test.assertSelectorContains('#computation-list-footer', '1');
+        test.assertExists('.js-queue.selected');
+      })
+
+      .then(function () {
+        casper.click('.js-history');
+        casper.waitForSelectorTextChange('#computation-list-footer');
+      })
+
+      .then(function () {
+        test.assertElementCount('#computation-list li[data-id]', 3);
+        test.assertSelectorContains('#computation-list', 'Duration');
+        test.assertExists('.js-history.selected');
+      })
+
+      .then(function () {
+        casper.click('.js-queue');
+        casper.waitForSelectorTextChange('#computation-list-footer');
+      })
+
+      .then(function () {
+        test.assertElementCount('#computation-list li[data-id]', 1);
+      })
+
+      .then(function () {
+        lib.sendCoverage();
+      })
+      .run(function () {
+        test.done();
+      });
+});
+
+
+casper.test.begin(testName('Show More'), 2, function (test) {
+  casper
+      .start(lib.buildUrl('computation#past'), function () {
+        lib.setDefaultViewport();
+        this.searchMock = lib.mockRequestFromFile('/api/computation/history', 'history-big-1.json');
+      })
+
+      .then(function () {
+        casper.evaluate(function () {
+          require(['apps/computation/app'], function (App) {
+            App.start({ el: '#computation' });
+          });
+        });
+      })
+
+      .then(function () {
+        casper.waitForSelector('#computation-list li');
+      })
+
+      .then(function () {
+        test.assertElementCount('#computation-list li[data-id]', 2);
+        lib.clearRequestMock(this.searchMock);
+        this.searchMock = lib.mockRequestFromFile('/api/computation/history', 'history-big-2.json',
+            { data: { p: '2' } });
+        casper.click('#computation-fetch-more');
+        casper.waitForSelectorTextChange('#computation-list-footer');
+      })
+
+      .then(function () {
+        test.assertElementCount('#computation-list li[data-id]', 3);
+      })
+
+      .then(function () {
+        lib.sendCoverage();
+      })
+      .run(function () {
+        test.done();
+      });
+});
diff --git a/server/sonar-web/src/test/json/computation-spec/history-big-1.json b/server/sonar-web/src/test/json/computation-spec/history-big-1.json
new file mode 100644 (file)
index 0000000..c7f53f3
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "total": 3,
+  "p": 1,
+  "ps": 2,
+  "reports": [
+    {
+      "submittedAt": "2015-05-07T14:25:20+0200",
+      "status": "SUCCESS",
+      "finishedAt": "2015-05-07T14:25:26+0200",
+      "startedAt": "2015-05-07T14:25:20+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "2"
+    },
+    {
+      "submittedAt": "2015-05-06T09:22:14+0200",
+      "status": "SUCCESS",
+      "finishedAt": "2015-05-06T09:22:18+0200",
+      "startedAt": "2015-05-06T09:22:14+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "1"
+    }
+  ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/history-big-2.json b/server/sonar-web/src/test/json/computation-spec/history-big-2.json
new file mode 100644 (file)
index 0000000..f6fea26
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "total": 3,
+  "p": 2,
+  "ps": 2,
+  "reports": [
+    {
+      "submittedAt": "2015-05-13T15:44:15+0200",
+      "status": "FAIL",
+      "finishedAt": "2015-05-13T15:44:18+0200",
+      "startedAt": "2015-05-13T15:44:15+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "3"
+    }
+  ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/history.json b/server/sonar-web/src/test/json/computation-spec/history.json
new file mode 100644 (file)
index 0000000..fbc51a0
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "total": 3,
+  "p": 1,
+  "ps": 10,
+  "reports": [
+    {
+      "submittedAt": "2015-05-13T15:44:15+0200",
+      "status": "FAIL",
+      "finishedAt": "2015-05-13T15:44:18+0200",
+      "startedAt": "2015-05-13T15:44:15+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "3"
+    },
+    {
+      "submittedAt": "2015-05-07T14:25:20+0200",
+      "status": "SUCCESS",
+      "finishedAt": "2015-05-07T14:25:26+0200",
+      "startedAt": "2015-05-07T14:25:20+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "2"
+    },
+    {
+      "submittedAt": "2015-05-06T09:22:14+0200",
+      "status": "SUCCESS",
+      "finishedAt": "2015-05-06T09:22:18+0200",
+      "startedAt": "2015-05-06T09:22:14+0200",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "1"
+    }
+  ]
+}
diff --git a/server/sonar-web/src/test/json/computation-spec/queue.json b/server/sonar-web/src/test/json/computation-spec/queue.json
new file mode 100644 (file)
index 0000000..18dcfbc
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "reports": [
+    {
+      "submittedAt": "2015-05-13T15:44:15+0200",
+      "status": "WORKING",
+      "projectUuid": "id-sonarqube",
+      "projectName": "SonarQube",
+      "projectKey": "key-sonarqube",
+      "key": "4"
+    }
+  ]
+}
diff --git a/server/sonar-web/src/test/views/computation.jade b/server/sonar-web/src/test/views/computation.jade
new file mode 100644 (file)
index 0000000..8145275
--- /dev/null
@@ -0,0 +1,5 @@
+extends layouts/main
+
+block body
+  #content
+    #computation
index e1891e03b55bcdfa153a1d536d3c876a350a5e04..901a104205594e6a09581504dabe8172f756a82c 100644 (file)
@@ -35,7 +35,7 @@ html
     script(src='/js/libs/graphics/pie-chart.js')
     script(src='/js/libs/graphics/barchart.js')
     script(src='/js/libs/sortable.js')
-    script(src='/js/components/common/inputs.js')
+    script(src='/js/libs/inputs.js')
     script(src='/js/components/common/dialogs.js')
     script(src='/js/components/common/processes.js')
     script(src='/js/components/common/jquery-isolated-scroll.js')