]> source.dussan.org Git - sonarqube.git/commitdiff
move issues page from coffee to js
authorStas Vilchik <vilchiks@gmail.com>
Mon, 1 Jun 2015 11:53:12 +0000 (13:53 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Mon, 1 Jun 2015 14:13:15 +0000 (16:13 +0200)
121 files changed:
server/sonar-web/Gruntfile.coffee
server/sonar-web/src/main/coffee/apps/issues/app-context.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/app-new.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/controller.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/layout.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/models/state.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/router.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs [deleted file]
server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee [deleted file]
server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee [deleted file]
server/sonar-web/src/main/js/apps/issues/app-context.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/app-new.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/component-viewer/main.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/controller.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/action-plan-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/assignee-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/author-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/base-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/context-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/custom-values-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/file-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/issue-key-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/language-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/module-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/project-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/reporter-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/resolution-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/rule-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/severity-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/status-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/facets/tag-facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/filters-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/issue-filter-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/layout.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/facet.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/facets.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/filter.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/filters.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/issues.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/models/state.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/router.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/_issues-filter-name.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/_issues-facet-header.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-action-plan-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-assignee-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-base-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-context-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-creation-date-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-custom-values-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-file-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-issue-key-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-resolution-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-severity-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/facets/issues-status-facet.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter-form.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-layout.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list-component.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/workspace-header-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/workspace-home-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/workspace-list-empty-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/workspace-list-view.js [new file with mode: 0644]

index 2339f6fe636223145665df8479804424d0c419ba..ff402d13cb06b7d30e534fcc1e3fdb695400cfff 100644 (file)
@@ -35,17 +35,6 @@ module.exports = (grunt) ->
           ]
 
 
-    coffee:
-      build:
-        files: [
-          expand: true
-          cwd: '<%= SOURCE_PATH %>/coffee'
-          src: ['**/*.coffee']
-          dest: '<%= BUILD_PATH %>/js'
-          ext: '.js'
-        ]
-
-
     concat:
       build:
         files:
@@ -206,7 +195,7 @@ module.exports = (grunt) ->
             '<%= SOURCE_PATH %>/js/components/issue/templates/**/*.hbs'
           ]
           '<%= BUILD_PATH %>/js/apps/issues/templates.js': [
-            '<%= SOURCE_PATH %>/coffee/apps/issues/templates/**/*.hbs'
+            '<%= SOURCE_PATH %>/js/apps/issues/templates/**/*.hbs'
           ]
           '<%= BUILD_PATH %>/js/apps/api-documentation/templates.js': [
             '<%= SOURCE_PATH %>/js/apps/api-documentation/templates/**/*.hbs'
@@ -383,10 +372,6 @@ module.exports = (grunt) ->
         files: '<%= SOURCE_PATH %>/less/**/*.less'
         tasks: ['less:build', 'copy:assets-css']
 
-      coffee:
-        files: '<%= SOURCE_PATH %>/coffee/**/*.coffee'
-        tasks: ['coffee:build', 'copy:js', 'concat:build', 'copy:assets-all-js']
-
       js:
         files: '<%= SOURCE_PATH %>/js/**/*.js'
         tasks: ['copy:js', 'concat:build', 'copy:assets-all-js']
@@ -398,7 +383,7 @@ module.exports = (grunt) ->
 
   # Basic tasks
   grunt.registerTask 'prepare',
-      ['clean:css', 'clean:js', 'clean:build', 'less:build', 'coffee:build', 'handlebars:build', 'copy:js', 'concat:build']
+      ['clean:css', 'clean:js', 'clean:build', 'less:build', 'handlebars:build', 'copy:js', 'concat:build']
 
   grunt.registerTask 'build-fast-suffix',
       ['copy:assets-css', 'copy:assets-all-js']
diff --git a/server/sonar-web/src/main/coffee/apps/issues/app-context.coffee b/server/sonar-web/src/main/coffee/apps/issues/app-context.coffee
deleted file mode 100644 (file)
index 556b93e..0000000
+++ /dev/null
@@ -1,131 +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 [
-  './models/state'
-  './layout'
-  './models/issues'
-  'components/navigator/models/facets'
-  './models/filters'
-
-  './controller'
-  './router'
-
-  './workspace-list-view'
-  './workspace-header-view'
-
-  './facets-view'
-  './filters-view'
-], (
-  State
-  Layout
-  Issues
-  Facets
-  Filters
-
-  Controller
-  Router
-
-  WorkspaceListView
-  WorkspaceHeaderView
-
-  FacetsView
-  FiltersView
-) ->
-
-  $ = jQuery
-  App = new Marionette.Application
-
-
-  App.getContextQuery = ->
-    componentUuids: window.config.resource
-
-
-  App.getRestrictedFacets = ->
-    'TRK': ['projectUuids']
-    'BRC': ['projectUuids']
-    'DIR': ['projectUuids', 'moduleUuids', 'directories']
-    'DEV': ['authors']
-    'DEV_PRJ': ['projectUuids', 'authors']
-
-
-  App.updateContextFacets = ->
-    facets = @state.get 'facets'
-    allFacets = @state.get 'allFacets'
-    facetsFromServer = @state.get 'facetsFromServer'
-    @state.set
-      facets: facets
-      allFacets: _.difference allFacets, @getRestrictedFacets()[window.config.resourceQualifier]
-      facetsFromServer: _.difference facetsFromServer, @getRestrictedFacets()[window.config.resourceQualifier]
-
-
-  App.addInitializer ->
-    @state = new State
-      isContext: true,
-      contextQuery: @getContextQuery()
-      contextComponentUuid: window.config.resource
-      contextComponentName: window.config.resourceName
-      contextComponentQualifier: window.config.resourceQualifier
-    @updateContextFacets()
-    @list = new Issues()
-    @facets = new Facets()
-    @filters = new Filters()
-
-
-  App.addInitializer ->
-    @layout = new Layout app: @
-    $('.issues').empty().append @layout.render().el
-    $('#footer').addClass('search-navigator-footer');
-
-
-  App.addInitializer ->
-    @controller = new Controller app: @
-
-
-  App.addInitializer ->
-    @issuesView = new WorkspaceListView
-      app: @
-      collection: @list
-    @layout.workspaceListRegion.show @issuesView
-    @issuesView.bindScrollEvents()
-
-
-  App.addInitializer ->
-    @workspaceHeaderView = new WorkspaceHeaderView
-      app: @
-      collection: @list
-    @layout.workspaceHeaderRegion.show @workspaceHeaderView
-
-
-  App.addInitializer ->
-    @facetsView = new FacetsView
-      app: @
-      collection: @facets
-    @layout.facetsRegion.show @facetsView
-
-
-  App.addInitializer ->
-    @controller.fetchFilters().done =>
-      key.setScope 'list'
-      @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/issues/app-new.coffee b/server/sonar-web/src/main/coffee/apps/issues/app-new.coffee
deleted file mode 100644 (file)
index c69a48e..0000000
+++ /dev/null
@@ -1,109 +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 [
-  './models/state'
-  './layout'
-  './models/issues'
-  'components/navigator/models/facets'
-  './models/filters'
-
-  './controller'
-  './router'
-
-  './workspace-list-view'
-  './workspace-header-view'
-
-  './facets-view'
-  './filters-view'
-], (
-  State
-  Layout
-  Issues
-  Facets
-  Filters
-
-  Controller
-  Router
-
-  WorkspaceListView
-  WorkspaceHeaderView
-
-  FacetsView
-  FiltersView
-) ->
-
-  $ = jQuery
-  App = new Marionette.Application
-
-  App.addInitializer ->
-    @state = new State()
-    @list = new Issues()
-    @facets = new Facets()
-    @filters = new Filters()
-
-
-  App.addInitializer ->
-    @layout = new Layout app: @
-    $('.issues').empty().append @layout.render().el
-    $('#footer').addClass('search-navigator-footer');
-
-
-  App.addInitializer ->
-    @controller = new Controller app: @
-
-
-  App.addInitializer ->
-    @issuesView = new WorkspaceListView
-      app: @
-      collection: @list
-    @layout.workspaceListRegion.show @issuesView
-    @issuesView.bindScrollEvents()
-
-
-  App.addInitializer ->
-    @workspaceHeaderView = new WorkspaceHeaderView
-      app: @
-      collection: @list
-    @layout.workspaceHeaderRegion.show @workspaceHeaderView
-
-
-  App.addInitializer ->
-    @facetsView = new FacetsView
-      app: @
-      collection: @facets
-    @layout.facetsRegion.show @facetsView
-
-
-  App.addInitializer ->
-    @filtersView = new FiltersView
-      app: @
-      collection: @filters
-    @layout.filtersRegion.show @filtersView
-
-
-  App.addInitializer ->
-    @controller.fetchFilters().done =>
-      key.setScope 'list'
-      @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/issues/component-viewer/issue-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/issue-view.coffee
deleted file mode 100644 (file)
index b626cff..0000000
+++ /dev/null
@@ -1,36 +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 [
-  '../workspace-list-item-view'
-], (
-  IssueView
-) ->
-
-  class extends IssueView
-
-    onRender: ->
-      super
-      @$el.removeClass 'issue-navigate-right'
-
-
-    serializeData: ->
-      _.extend super,
-        showComponent: false
diff --git a/server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee b/server/sonar-web/src/main/coffee/apps/issues/component-viewer/main.coffee
deleted file mode 100644 (file)
index 1a23d07..0000000
+++ /dev/null
@@ -1,192 +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 [
-  'components/source-viewer/main'
-  '../models/issues'
-  './issue-view'
-  '../templates'
-], (
-  SourceViewer
-  Issues
-  IssueView
-) ->
-
-  $ = jQuery
-
-  TOP_OFFSET = 38
-  BOTTOM_OFFSET = 10
-
-
-  class extends SourceViewer
-
-    events: ->
-      _.extend super,
-        'click .js-close-component-viewer': 'closeComponentViewer'
-        'click .code-issue': 'selectIssue'
-
-
-    initialize: (options) ->
-      super
-      @listenTo options.app.state, 'change:selectedIndex', @select
-
-
-    onLoaded: ->
-      super
-      @bindShortcuts()
-      if @baseIssue?
-        @scrollToLine @baseIssue.get 'line'
-
-
-    bindShortcuts: ->
-      doAction = (action) =>
-        selectedIssueView = @getSelectedIssueEl()
-        return unless selectedIssueView
-        selectedIssueView.find(".js-issue-#{action}").click()
-
-      key 'up', 'componentViewer', =>
-        @options.app.controller.selectPrev()
-        false
-
-      key 'down', 'componentViewer', =>
-        @options.app.controller.selectNext()
-        false
-
-      key 'left,backspace', 'componentViewer', =>
-        @options.app.controller.closeComponentViewer()
-        false
-
-      key 'f', 'componentViewer', -> doAction 'transition'
-      key 'a', 'componentViewer', -> doAction 'assign'
-      key 'm', 'componentViewer', -> doAction 'assign-to-me'
-      key 'p', 'componentViewer', -> doAction 'plan'
-      key 'i', 'componentViewer', -> doAction 'set-severity'
-      key 'c', 'componentViewer', -> doAction 'comment'
-
-
-    unbindShortcuts: ->
-      key.deleteScope 'componentViewer'
-
-
-    onClose: ->
-      super
-      @unbindScrollEvents()
-      @unbindShortcuts()
-
-
-    select: ->
-      selected = @options.app.state.get 'selectedIndex'
-      selectedIssue = @options.app.list.at selected
-      if selectedIssue.get('component') == @model.get('key')
-        @scrollToIssue selectedIssue.get('key')
-      else
-        @unbindShortcuts()
-        @options.app.controller.showComponentViewer selectedIssue
-
-
-    getSelectedIssueEl: ->
-      selected = @options.app.state.get 'selectedIndex'
-      return null unless selected?
-      selectedIssue = @options.app.list.at selected
-      return null unless selectedIssue?
-      selectedIssueView = @$("#issue-#{selectedIssue.get('key')}")
-      if selectedIssueView.length > 0 then selectedIssueView else null
-
-
-    selectIssue: (e) ->
-      key = $(e.currentTarget).data 'issue-key'
-      issue = @issues.find (issue) -> issue.get('key') == key
-      index = @options.app.list.indexOf issue
-      @options.app.state.set selectedIndex: index
-
-
-    scrollToIssue: (key) ->
-      el = @$("#issue-#{key}")
-      if el.length > 0
-        line = el.closest('[data-line-number]').data 'line-number'
-        this.scrollToLine line
-      else
-        @unbindShortcuts()
-        selected = @options.app.state.get 'selectedIndex'
-        selectedIssue = @options.app.list.at selected
-        @options.app.controller.showComponentViewer selectedIssue
-
-
-    openFileByIssue: (issue) ->
-      @baseIssue = issue
-      componentKey = issue.get 'component'
-      componentUuid = issue.get 'componentUuid'
-      @open componentUuid, componentKey
-
-
-    linesLimit: ->
-      line = @LINES_LIMIT / 2
-      if @baseIssue? && @baseIssue.has('line')
-        line = Math.max line, @baseIssue.get('line')
-      from: line - @LINES_LIMIT / 2 + 1
-      to: line + @LINES_LIMIT / 2
-
-
-    limitIssues: (issues) ->
-      index = @ISSUES_LIMIT / 2
-      if @baseIssue? && @baseIssue.has('index')
-        index = Math.max index, @baseIssue.get('index')
-      x = issues.filter (issue) =>
-        Math.abs(issue.get('index') - index) <= @ISSUES_LIMIT / 2
-      x
-
-
-    requestIssues: ->
-      if @options.app.list.last().get('component') == @model.get('key')
-        r = @options.app.controller.fetchNextPage()
-      else r = $.Deferred().resolve().promise()
-      r.done =>
-        @issues.reset @options.app.list.filter (issue) => issue.get('component') == @model.key()
-        @issues.reset @limitIssues @issues
-        @addIssuesPerLineMeta @issues
-
-
-    renderIssues: ->
-      @issues.forEach @renderIssue, @
-      @$('.source-line-issues').addClass('hidden');
-
-
-    renderIssue: (issue) ->
-      issueView = new IssueView
-        el: '#issue-' + issue.get('key')
-        model: issue
-        app: @options.app
-      @issueViews.push issueView
-      issueView.render()
-
-
-    addNextIssuesPage: ->
-      componentKey = @model.get 'key'
-      @issues.add @options.app.list.filter (issue) => issue.get('component') == componentKey
-
-
-    scrollToLine: (line) ->
-      row = @$("[data-line-number=#{line}]")
-      goal = if row.length > 0 then row.offset().top - 200 else 0
-      $(window).scrollTop goal
-
-
-    closeComponentViewer: ->
-      @options.app.controller.closeComponentViewer()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/controller.coffee b/server/sonar-web/src/main/coffee/apps/issues/controller.coffee
deleted file mode 100644 (file)
index 18749e8..0000000
+++ /dev/null
@@ -1,225 +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 [
-  'components/navigator/controller'
-
-  './component-viewer/main'
-  './workspace-home-view'
-], (
-  Controller
-
-  ComponentViewer
-  HomeView
-) ->
-
-  $ = jQuery
-  EXTRA_FIELDS = 'actions,transitions,assigneeName,reporterName,actionPlanName'
-  FACET_DATA_FIELDS = ['components', 'projects', 'users', 'rules', 'actionPlans', 'languages']
-
-
-  class extends Controller
-
-    _facetsFromServer: ->
-      facets = super || []
-      facets.push 'assigned_to_me'
-      facets
-
-
-    _issuesParameters: ->
-      p: @options.app.state.get 'page'
-      ps: @pageSize
-      s: 'FILE_LINE'
-      asc: true
-      extra_fields: EXTRA_FIELDS
-      facets: @_facetsFromServer().join()
-
-
-    _myIssuesFromResponse: (r) ->
-      myIssuesData = _.findWhere r.facets, property: 'assigned_to_me'
-      if myIssuesData? && _.isArray(myIssuesData.values) && myIssuesData.values.length > 0
-        @options.app.state.set { myIssues: myIssuesData.values[0].count }, { silent: true }
-      else
-        @options.app.state.unset 'myIssues', { silent: true }
-
-
-    fetchList: (firstPage = true) ->
-      if firstPage
-        @options.app.state.set { selectedIndex: 0, page: 1 }, { silent: true }
-        @hideHomePage()
-        @closeComponentViewer()
-
-      data = @_issuesParameters()
-      _.extend data, @options.app.state.get 'query'
-      _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
-
-      $.get "#{baseUrl}/api/issues/search", data
-      .done (r) =>
-        issues = @options.app.list.parseIssues r
-        if firstPage
-          @options.app.list.reset issues
-        else
-          @options.app.list.add issues
-        @options.app.list.setIndex()
-        FACET_DATA_FIELDS.forEach (field) => @options.app.facets[field] = r[field]
-        @options.app.facets.reset @_allFacets()
-        @options.app.facets.add _.reject(r.facets, (f) -> f.property == 'assigned_to_me'), merge: true
-        @_myIssuesFromResponse r
-        @enableFacets @_enabledFacets()
-        @options.app.state.set
-          page: r.p
-          pageSize: r.ps
-          total: r.total
-          maxResultsReached: r.p * r.ps >= r.total
-        if firstPage && @isIssuePermalink()
-          @showComponentViewer @options.app.list.first()
-
-
-    isIssuePermalink: ->
-      query = @options.app.state.get('query')
-      query.issues? && @options.app.list.length == 1
-
-
-    fetchFilters: ->
-      $.get "#{baseUrl}/api/issue_filters/app", (r) =>
-        @options.app.state.set
-          canBulkChange: r.canBulkChange
-          canManageFilters: r.canManageFilters
-        @options.app.filters.reset r.favorites
-
-
-    _mergeCollections: (a, b) ->
-      collection = new Backbone.Collection a
-      collection.add b, merge: true
-      collection.toJSON()
-
-
-    requestFacet: (id) ->
-      return @requestAssigneeFacet() if id == 'assignees'
-      facet = @options.app.facets.get id
-      data = _.extend { facets: id, ps: 1 }, @options.app.state.get('query')
-      _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
-      $.get "#{baseUrl}/api/issues/search", data, (r) =>
-        FACET_DATA_FIELDS.forEach (field) =>
-          @options.app.facets[field] = @_mergeCollections @options.app.facets[field], r[field]
-        facetData = _.findWhere r.facets, property: id
-        facet.set facetData if facetData?
-
-
-    requestAssigneeFacet: ->
-      facet = @options.app.facets.get 'assignees'
-      data = _.extend { facets: 'assignees,assigned_to_me', ps: 1 }, @options.app.state.get('query')
-      _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext'
-      $.get "#{baseUrl}/api/issues/search", data, (r) =>
-        FACET_DATA_FIELDS.forEach (field) =>
-          @options.app.facets[field] = @_mergeCollections @options.app.facets[field], r[field]
-        facetData = _.findWhere r.facets, property: 'assignees'
-        @_myIssuesFromResponse r
-        facet.set facetData if facetData?
-
-
-    newSearch: ->
-      @options.app.state.unset 'filter'
-      @options.app.state.setQuery resolved: 'false'
-
-
-    applyFilter: (filter, ignoreQuery = false) ->
-      unless ignoreQuery
-        filterQuery = @parseQuery filter.get 'query'
-        @options.app.state.setQuery filterQuery
-      @options.app.state.set filter: filter, changed: false
-
-
-    parseQuery: ->
-      q = super
-      # Do not allow to modify the sorting
-      delete q.asc
-      delete q.s
-      q
-
-
-    getQuery: (separator = '|', addContext = false) ->
-      filter = @options.app.state.get 'query'
-      _.extend filter, @options.app.state.get 'contextQuery' if addContext && @options.app.state.get 'isContext'
-      route = []
-      _.map filter, (value, property) ->
-        route.push '' + property + '=' + encodeURIComponent(value)
-      route.join separator
-
-
-    getRoute: ->
-      filter = @options.app.state.get 'filter'
-      query = super
-      if filter?
-        if @options.app.state.get('changed') && query.length > 0
-          query = "id=#{filter.id}|#{query}"
-        else
-          query = "id=#{filter.id}"
-      query
-
-
-    _prepareComponent: (issue) ->
-      key: issue.get 'component'
-      name: issue.get 'componentLongName'
-      qualifier: issue.get 'componentQualifier'
-      project: issue.get 'project'
-      projectName: issue.get 'projectLongName'
-
-
-    showComponentViewer: (issue) ->
-      @options.app.layout.workspaceComponentViewerRegion.reset()
-      key.setScope 'componentViewer'
-      @options.app.issuesView.unbindScrollEvents()
-      @options.app.state.set 'component', @_prepareComponent(issue)
-      @options.app.componentViewer = new ComponentViewer app: @options.app
-      @options.app.layout.workspaceComponentViewerRegion.show @options.app.componentViewer
-      @options.app.layout.showComponentViewer()
-      @options.app.componentViewer.openFileByIssue issue
-
-
-    closeComponentViewer: ->
-      key.setScope 'list'
-      # close all popups
-      $('body').click()
-      @options.app.state.unset 'component'
-      @options.app.layout.workspaceComponentViewerRegion.reset()
-      @options.app.layout.hideComponentViewer()
-      @options.app.issuesView.bindScrollEvents()
-      @options.app.issuesView.scrollTo()
-
-
-    showHomePage: ->
-      @fetchList()
-      @options.app.layout.workspaceComponentViewerRegion.reset()
-      key.setScope 'home'
-      @options.app.issuesView.unbindScrollEvents()
-      @options.app.homeView = new HomeView app: @options.app
-      @options.app.layout.workspaceHomeRegion.show @options.app.homeView
-      @options.app.layout.showHomePage()
-
-
-    hideHomePage: ->
-      @options.app.layout.workspaceComponentViewerRegion.reset()
-      @options.app.layout.workspaceHomeRegion.reset()
-      key.setScope 'list'
-      @options.app.layout.hideHomePage()
-      @options.app.issuesView.bindScrollEvents()
-      @options.app.issuesView.scrollTo()
-
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets-view.coffee
deleted file mode 100644 (file)
index a8a1794..0000000
+++ /dev/null
@@ -1,83 +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 [
-  'components/navigator/facets-view'
-
-  './facets/base-facet'
-  './facets/severity-facet'
-  './facets/status-facet'
-  './facets/project-facet'
-  './facets/module-facet'
-  './facets/assignee-facet'
-  './facets/rule-facet'
-  './facets/tag-facet'
-  './facets/resolution-facet'
-  './facets/creation-date-facet'
-  './facets/action-plan-facet'
-  './facets/file-facet'
-  './facets/reporter-facet'
-  './facets/language-facet'
-  './facets/author-facet'
-  './facets/issue-key-facet'
-  './facets/context-facet'
-], (
-  FacetsView
-
-  BaseFacet
-  SeverityFacet
-  StatusFacet
-  ProjectFacet
-  ModuleFacet
-  AssigneeFacet
-  RuleFacet
-  TagFacet
-  ResolutionFacet
-  CreationDateFacet
-  ActionPlanFacet
-  FileFacet
-  ReporterFacet
-  LanguageFacet
-  AuthorFacet
-  IssueKeyFacet
-  ContextFacet
-) ->
-
-  class extends FacetsView
-
-    getItemView: (model) ->
-      switch model.get 'property'
-        when 'severities' then SeverityFacet
-        when 'statuses' then StatusFacet
-        when 'assignees' then AssigneeFacet
-        when 'resolutions' then ResolutionFacet
-        when 'createdAt' then CreationDateFacet
-        when 'projectUuids' then ProjectFacet
-        when 'moduleUuids' then ModuleFacet
-        when 'rules' then RuleFacet
-        when 'tags' then TagFacet
-        when 'actionPlans' then ActionPlanFacet
-        when 'fileUuids' then FileFacet
-        when 'reporters' then ReporterFacet
-        when 'languages' then LanguageFacet
-        when 'authors' then AuthorFacet
-        when 'issues' then IssueKeyFacet
-        when 'context' then ContextFacet
-        else BaseFacet
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/action-plan-facet.coffee
deleted file mode 100644 (file)
index a2a746e..0000000
+++ /dev/null
@@ -1,72 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-  $ = jQuery
-
-
-  class extends BaseFacet
-    template: Templates['issues-action-plan-facet']
-
-
-    onRender: ->
-      super
-      value = @options.app.state.get('query')['planned']
-      if value? && (!value || value == 'false')
-        @$('.js-facet').filter("[data-unplanned]").addClass 'active'
-
-
-    toggleFacet: (e) ->
-      unplanned = $(e.currentTarget).is "[data-unplanned]"
-      $(e.currentTarget).toggleClass 'active'
-      if unplanned
-        checked = $(e.currentTarget).is '.active'
-        value = if checked then 'false' else null
-        @options.app.state.updateFilter planned: value, actionPlans: null
-      else
-        @options.app.state.updateFilter planned: null, actionPlans: @getValue()
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      actionPlans = @options.app.facets.actionPlans
-      values.forEach (v) =>
-        key = v.val
-        label = null
-        if key
-          actionPlan = _.findWhere actionPlans, key: key
-          label = actionPlan.name if actionPlan?
-        v.label = label
-      values
-
-
-    disable: ->
-      @options.app.state.updateFilter planned: null, actionPlans: null
-
-
-    serializeData: ->
-      _.extend super,
-        values: @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/assignee-facet.coffee
deleted file mode 100644 (file)
index 83d0a78..0000000
+++ /dev/null
@@ -1,105 +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 [
-  './custom-values-facet'
-  '../templates'
-], (
-  CustomValuesFacet
-) ->
-
-  $ = jQuery
-
-
-  class extends CustomValuesFacet
-    template: Templates['issues-assignee-facet']
-
-
-    getUrl: ->
-      "#{baseUrl}/api/users/search"
-
-    prepareAjaxSearch: ->
-      quietMillis: 300
-      url: @getUrl()
-      data: (term, page) -> { q: term, p: page }
-      results: window.usersToSelect2
-
-    onRender: ->
-      super
-      value = @options.app.state.get('query')['assigned']
-      if value? && (!value || value == 'false')
-        @$('.js-facet').filter("[data-unassigned]").addClass 'active'
-
-
-    toggleFacet: (e) ->
-      unassigned = $(e.currentTarget).is "[data-unassigned]"
-      $(e.currentTarget).toggleClass 'active'
-      if unassigned
-        checked = $(e.currentTarget).is '.active'
-        value = if checked then 'false' else null
-        @options.app.state.updateFilter assigned: value, assignees: null
-      else
-        @options.app.state.updateFilter assigned: null, assignees: @getValue()
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      users = @options.app.facets.users
-      values.forEach (v) =>
-        login = v.val
-        name = ''
-        if login
-          user = _.findWhere users, login: login
-          name = user.name if user?
-        v.label = name
-      values
-
-
-    disable: ->
-      @options.app.state.updateFilter assigned: null, assignees: null
-
-
-    addCustomValue: ->
-      property = @model.get 'property'
-      customValue = @$('.js-custom-value').select2 'val'
-      value = @getValue()
-      value += ',' if value.length > 0
-      value += customValue
-      obj = {}
-      obj[property] = value
-      obj.assigned = null
-      @options.app.state.updateFilter obj
-
-
-    sortValues: (values) ->
-      # put "unassigned" first
-      _.sortBy values, (v) ->
-        x = if v.val == '' then -999999 else -v.count
-        x
-
-
-    getNumberOfMyIssues: ->
-      @options.app.state.get 'myIssues'
-
-
-    serializeData: ->
-      _.extend super,
-        myIssues: @getNumberOfMyIssues()
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/author-facet.coffee
deleted file mode 100644 (file)
index 25e8c98..0000000
+++ /dev/null
@@ -1,47 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    getUrl: ->
-      "#{baseUrl}/api/issues/authors"
-
-
-    prepareSearch: ->
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 2
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        formatInputTooShort: -> tp 'select2.tooShort', 2
-        width: '100%'
-        ajax:
-          quietMillis: 300
-          url: @getUrl()
-          data: (term) -> { q: term, ps: 25 }
-          results: (data) -> { more: false, results: data.authors.map (author) -> { id: author, text: author } }
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/base-facet.coffee
deleted file mode 100644 (file)
index 4d41996..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 [
-  'components/navigator/facets/base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-  class extends BaseFacet
-    template: Templates['issues-base-facet']
-
-    onRender: ->
-      super
-      @$('[data-toggle="tooltip"]').tooltip container: 'body'
-
-
-    onClose: ->
-      @$('[data-toggle="tooltip"]').tooltip 'destroy'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/context-facet.coffee
deleted file mode 100644 (file)
index 3809f07..0000000
+++ /dev/null
@@ -1,35 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-
-  class extends BaseFacet
-    template: Templates['issues-context-facet']
-
-
-    serializeData: ->
-      _.extend super,
-          state: @options.app.state.toJSON()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/creation-date-facet.coffee
deleted file mode 100644 (file)
index c1a2901..0000000
+++ /dev/null
@@ -1,141 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-  $ = jQuery
-
-
-  class extends BaseFacet
-    template: Templates['issues-creation-date-facet']
-
-
-    events: ->
-      _.extend super,
-        'change input': 'applyFacet'
-        'click .js-select-period-start': 'selectPeriodStart'
-        'click .js-select-period-end': 'selectPeriodEnd'
-
-        'click .sonar-d3 rect': 'selectBar'
-
-        'click .js-all': 'onAllClick'
-        'click .js-last-week': 'onLastWeekClick'
-        'click .js-last-month': 'onLastMonthClick'
-        'click .js-last-year': 'onLastYearClick'
-
-
-    onRender: ->
-      @$el.toggleClass 'search-navigator-facet-box-collapsed', !@model.get('enabled')
-
-      @$('input').datepicker
-        dateFormat: 'yy-mm-dd'
-        changeMonth: true
-        changeYear: true
-
-      props = ['createdAfter', 'createdBefore', 'createdAt']
-      query = @options.app.state.get 'query'
-      props.forEach (prop) =>
-        value = query[prop]
-        @$("input[name=#{prop}]").val value if value?
-
-      values = @model.getValues()
-      unless _.isArray(values) && values.length > 0
-        date = moment()
-        values = []
-        for i in [0..10]
-          values.push count: 0, val: date.toDate().toString()
-          date = date.subtract 1, 'days'
-        values.reverse()
-      @$('.js-barchart').barchart values
-
-
-    selectPeriodStart: ->
-      @$('.js-period-start').datepicker 'show'
-
-
-    selectPeriodEnd: ->
-      @$('.js-period-end').datepicker 'show'
-
-
-    applyFacet: ->
-      obj = createdAt: null, createdInLast: null
-      @$('input').each ->
-        property = $(@).prop 'name'
-        value = $(@).val()
-        obj[property] = value
-      @options.app.state.updateFilter obj
-
-
-    disable: ->
-      @options.app.state.updateFilter
-        createdAfter: null
-        createdBefore: null
-        createdAt: null
-        createdInLast: null
-
-
-    selectBar: (e) ->
-      periodStart = $(e.currentTarget).data 'period-start'
-      periodEnd = $(e.currentTarget).data 'period-end'
-      @options.app.state.updateFilter
-        createdAfter: periodStart
-        createdBefore: periodEnd
-        createdAt: null
-        createdInLast: null
-
-
-    selectPeriod: (period) ->
-      @options.app.state.updateFilter
-        createdAfter: null
-        createdBefore: null
-        createdAt: null
-        createdInLast: period
-
-
-    onAllClick: ->
-      @disable()
-
-
-    onLastWeekClick: (e) ->
-      e.preventDefault()
-      @selectPeriod '1w'
-
-
-    onLastMonthClick: (e) ->
-      e.preventDefault()
-      @selectPeriod '1m'
-
-
-    onLastYearClick: (e) ->
-      e.preventDefault()
-      @selectPeriod '1y'
-
-
-    serializeData: ->
-      _.extend super,
-        periodStart: @options.app.state.get('query').createdAfter
-        periodEnd: @options.app.state.get('query').createdBefore
-        createdAt: @options.app.state.get('query').createdAt
-        createdInLast: @options.app.state.get('query').createdInLast
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/custom-values-facet.coffee
deleted file mode 100644 (file)
index 9cd45d0..0000000
+++ /dev/null
@@ -1,71 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-
-  class extends BaseFacet
-    template: Templates['issues-custom-values-facet']
-
-
-    events: ->
-      _.extend super,
-        'change .js-custom-value': 'addCustomValue'
-
-
-    getUrl: ->
-
-
-    onRender: ->
-      super
-      @prepareSearch()
-
-
-    prepareSearch: ->
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 2
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        formatInputTooShort: -> tp 'select2.tooShort', 2
-        width: '100%'
-        ajax: @prepareAjaxSearch()
-
-    prepareAjaxSearch: ->
-      quietMillis: 300
-      url: @getUrl()
-      data: (term, page) -> { s: term, p: page }
-      results: (data) -> { more: data.more, results: data.results }
-
-    addCustomValue: ->
-      property = @model.get 'property'
-      customValue = @$('.js-custom-value').select2 'val'
-      value = @getValue()
-      value += ',' if value.length > 0
-      value += customValue
-      obj = {}
-      obj[property] = value
-      @options.app.state.updateFilter obj
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/file-facet.coffee
deleted file mode 100644 (file)
index 6208144..0000000
+++ /dev/null
@@ -1,56 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-  $ = jQuery
-
-
-  class extends BaseFacet
-    template: Templates['issues-file-facet']
-
-
-    onRender: ->
-      super
-      maxValueWidth = _.max @$('.facet-stat').map(-> $(@).outerWidth()).get()
-      @$('.facet-name').css 'padding-right', maxValueWidth
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      source = @options.app.facets.components
-      values.forEach (v) =>
-        key = v.val
-        label = null
-        if key
-          item = _.findWhere source, uuid: key
-          label = item.longName if item?
-        v.label = label
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/issue-key-facet.coffee
deleted file mode 100644 (file)
index 53afcf4..0000000
+++ /dev/null
@@ -1,43 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-
-  class extends BaseFacet
-    template: Templates['issues-issue-key-facet']
-
-
-    onRender: ->
-      @$el.toggleClass 'hidden', !@options.app.state.get('query').issues
-
-
-    disable: ->
-      @options.app.state.updateFilter issues: null
-
-
-    serializeData: ->
-      _.extend super,
-        issues: @options.app.state.get('query').issues
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/language-facet.coffee
deleted file mode 100644 (file)
index 3d67584..0000000
+++ /dev/null
@@ -1,67 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    getUrl: ->
-      "#{baseUrl}/api/languages/list"
-
-
-    prepareSearch: ->
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 2
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        formatInputTooShort: -> tp 'select2.tooShort', 2
-        width: '100%'
-        ajax:
-          quietMillis: 300
-          url: @getUrl()
-          data: (term) -> { q: term, ps: 0 }
-          results: (data) ->
-            more: false
-            results: data.languages.map (lang) -> { id: lang.key, text: lang.name }
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      source = @options.app.facets.languages
-      values.forEach (v) =>
-        key = v.val
-        label = null
-        if key
-          item = _.findWhere source, key: key
-          label = item.name if item?
-        v.label = label
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/module-facet.coffee
deleted file mode 100644 (file)
index 425f4b5..0000000
+++ /dev/null
@@ -1,44 +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 [
-  './base-facet'
-], (
-  BaseFacet
-) ->
-
-  class extends BaseFacet
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      components = @options.app.facets.components
-      values.forEach (v) =>
-        uuid = v.val
-        label = uuid
-        if uuid
-          component = _.findWhere components, uuid: uuid
-          label = component.longName if component?
-        v.label = label
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/project-facet.coffee
deleted file mode 100644 (file)
index d7ea58a..0000000
+++ /dev/null
@@ -1,83 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    getUrl: ->
-      q = @options.app.state.get 'contextComponentQualifier'
-      if q == 'VW' || q == 'SVW'
-        "#{baseUrl}/api/components/search"
-      else
-        "#{baseUrl}/api/resources/search?f=s2&q=TRK&display_uuid=true"
-
-
-    prepareSearch: ->
-      q = @options.app.state.get 'contextComponentQualifier'
-      if q == 'VW' || q == 'SVW'
-        @prepareSearchForViews()
-      else super
-
-
-    prepareSearchForViews: ->
-      componentUuid = this.options.app.state.get 'contextComponentUuid'
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 2
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        formatInputTooShort: -> tp 'select2.tooShort', 2
-        width: '100%'
-        ajax:
-          quietMillis: 300
-          url: @getUrl()
-          data: (term, page) ->
-            q: term
-            componentUuid: componentUuid
-            p: page
-            ps: 25
-          results: (data) ->
-            more: data.p * data.ps < data.total,
-            results: data.components.map (c) -> id: c.uuid, text: c.name
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      projects = @options.app.facets.projects
-      values.forEach (v) =>
-        uuid = v.val
-        label = ''
-        if uuid
-          project = _.findWhere projects, uuid: uuid
-          label = project.longName if project?
-        v.label = label
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/reporter-facet.coffee
deleted file mode 100644 (file)
index e8e10a4..0000000
+++ /dev/null
@@ -1,54 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    getUrl: ->
-      "#{baseUrl}/api/users/search"
-
-    prepareAjaxSearch: ->
-      quietMillis: 300
-      url: @getUrl()
-      data: (term, page) -> { q: term, p: page }
-      results: window.usersToSelect2
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      source = @options.app.facets.users
-      values.forEach (v) =>
-        key = v.val
-        label = null
-        if key
-          item = _.findWhere source, login: key
-          label = item.name if item?
-        v.label = label
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/resolution-facet.coffee
deleted file mode 100644 (file)
index 7c7eef0..0000000
+++ /dev/null
@@ -1,61 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-  $ = jQuery
-
-
-  class extends BaseFacet
-    template: Templates['issues-resolution-facet']
-
-
-    onRender: ->
-      super
-
-      value = @options.app.state.get('query')['resolved']
-      if value? && (!value || value == 'false')
-        @$('.js-facet').filter("[data-unresolved]").addClass 'active'
-
-
-    toggleFacet: (e) ->
-      unresolved = $(e.currentTarget).is "[data-unresolved]"
-      $(e.currentTarget).toggleClass 'active'
-      if unresolved
-        checked = $(e.currentTarget).is '.active'
-        value = if checked then 'false' else null
-        @options.app.state.updateFilter resolved: value, resolutions: null
-      else
-        @options.app.state.updateFilter resolved: null, resolutions: @getValue()
-
-
-    disable: ->
-      @options.app.state.updateFilter resolved: null, resolutions: null
-
-
-    sortValues: (values) ->
-      order = ['', 'FIXED', 'FALSE-POSITIVE', 'WONTFIX', 'REMOVED']
-      _.sortBy values, (v) -> order.indexOf v.val
-
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/rule-facet.coffee
deleted file mode 100644 (file)
index 25ea9f9..0000000
+++ /dev/null
@@ -1,71 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    prepareSearch: ->
-      url = "#{baseUrl}/api/rules/search?f=name,langName"
-      languages = @options.app.state.get('query').languages
-      if languages?
-        url += "&languages=#{languages}"
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 2
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        formatInputTooShort: -> tp 'select2.tooShort', 2
-        width: '100%'
-        ajax:
-          quietMillis: 300
-          url: url
-          data: (term, page) -> { q: term, p: page }
-          results: (data) ->
-            results = data.rules.map (rule) ->
-              id: rule.key, text: "(#{rule.langName}) #{rule.name}"
-            { more: (data.p * data.ps < data.total), results: results }
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      rules = @options.app.facets.rules
-      values.forEach (v) =>
-        key = v.val
-        label = ''
-        extra = ''
-        if key
-          rule = _.findWhere rules, key: key
-          label = rule.name if rule?
-          extra = rule.langName if rule?
-        v.label = label
-        v.extra = extra
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/severity-facet.coffee
deleted file mode 100644 (file)
index c0639ef..0000000
+++ /dev/null
@@ -1,36 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-
-  class extends BaseFacet
-    template: Templates['issues-severity-facet']
-
-
-    sortValues: (values) ->
-      order = ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR']
-      _.sortBy values, (v) -> order.indexOf v.val
-
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/status-facet.coffee
deleted file mode 100644 (file)
index f3a0eb7..0000000
+++ /dev/null
@@ -1,35 +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 [
-  './base-facet'
-  '../templates'
-], (
-  BaseFacet
-) ->
-
-
-  class extends BaseFacet
-    template: Templates['issues-status-facet']
-
-
-    sortValues: (values) ->
-      order = ['OPEN', 'RESOLVED', 'REOPENED', 'CLOSED', 'CONFIRMED']
-      _.sortBy values, (v) -> order.indexOf v.val
diff --git a/server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/facets/tag-facet.coffee
deleted file mode 100644 (file)
index fb061cf..0000000
+++ /dev/null
@@ -1,63 +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 [
-  './custom-values-facet'
-], (
-  CustomValuesFacet
-) ->
-
-
-  class extends CustomValuesFacet
-
-    prepareSearch: ->
-      url = "#{baseUrl}/api/issues/tags?ps=10"
-      tags = @options.app.state.get('query').tags
-      if tags?
-        url += "&tags=#{tags}"
-      @$('.js-custom-value').select2
-        placeholder: 'Search...'
-        minimumInputLength: 0
-        allowClear: false
-        formatNoMatches: -> t 'select2.noMatches'
-        formatSearching: -> t 'select2.searching'
-        width: '100%'
-        ajax:
-          quietMillis: 300
-          url: url
-          data: (term, page) -> { q: term, ps: 10 }
-          results: (data) ->
-            results = data.tags.map (tag) ->
-              id: tag, text: tag
-            { more: false, results: results }
-
-
-    getValuesWithLabels: ->
-      values = @model.getValues()
-      tags = @options.app.facets.tags
-      values.forEach (v) =>
-        v.label = v.val
-        v.extra = ''
-      values
-
-
-    serializeData: ->
-      _.extend super,
-        values: @sortValues @getValuesWithLabels()
diff --git a/server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/filters-view.coffee
deleted file mode 100644 (file)
index 7102431..0000000
+++ /dev/null
@@ -1,97 +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'
-], ->
-
-  $ = jQuery
-
-
-  class extends Marionette.ItemView
-    template: Templates['issues-filters']
-
-
-    events:
-      'click .js-toggle-filters': 'toggleFilters'
-      'click .js-filter': 'applyFilter'
-      'click .js-filter-save-as': 'saveAs'
-      'click .js-filter-save': 'save'
-      'click .js-filter-copy': 'copy'
-      'click .js-filter-edit': 'edit'
-
-
-    initialize: (options) ->
-      @listenTo options.app.state, 'change:filter', @render
-      @listenTo options.app.state, 'change:changed', @render
-      @listenTo options.app.filters, 'all', @render
-      window.onSaveAs = window.onCopy = window.onEdit = (id) =>
-        $('#modal').dialog 'close'
-        @options.app.controller.fetchFilters().done =>
-          filter = @collection.get id
-          filter.fetch().done => @options.app.controller.applyFilter filter
-
-
-    onRender: ->
-      @$el.toggleClass 'search-navigator-filters-selected', @options.app.state.has('filter')
-
-
-    toggleFilters: (e) ->
-      e.stopPropagation()
-      @$('.search-navigator-filters-list').toggle()
-      $('body').on 'click.issues-filters', =>
-        $('body').off 'click.issues-filters'
-        @$('.search-navigator-filters-list').hide()
-
-
-    applyFilter: (e) ->
-      id = $(e.currentTarget).data 'id'
-      filter = @collection.get id
-      filter.fetch().done => @options.app.controller.applyFilter filter
-
-
-    saveAs: ->
-      query = @options.app.controller.getQuery '&'
-      url = "#{baseUrl}/issues/save_as_form?#{query}"
-      openModalWindow url, {}
-
-
-    save: ->
-      query = @options.app.controller.getQuery '&'
-      url = "#{baseUrl}/issues/save/#{@options.app.state.get('filter').id}?#{query}"
-      $.post(url).done =>
-        @options.app.state.set changed: false
-
-
-    copy: ->
-      url = "#{baseUrl}/issues/copy_form/#{@options.app.state.get('filter').id}"
-      openModalWindow url, {}
-
-
-    edit: ->
-      url = "#{baseUrl}/issues/edit_form/#{@options.app.state.get('filter').id}"
-      openModalWindow url, {}
-
-
-    serializeData: ->
-      _.extend super,
-        state: @options.app.state.toJSON()
-        filter: @options.app.state.get('filter')?.toJSON()
-        currentUser: window.SS.user
diff --git a/server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/issue-filter-view.coffee
deleted file mode 100644 (file)
index cb3ee4d..0000000
+++ /dev/null
@@ -1,47 +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 [
-  'components/issue/views/action-options-view'
-  './templates'
-], (
-  ActionOptionsView
-) ->
-
-  $ = jQuery
-
-
-  class extends ActionOptionsView
-    template: Templates['issues-issue-filter-form']
-
-
-    selectInitialOption: ->
-      @makeActive @getOptions().first()
-
-
-    selectOption: (e) ->
-      property = $(e.currentTarget).data 'property'
-      value = $(e.currentTarget).data 'value'
-      @trigger 'select', property, value
-      super
-
-
-    serializeData: ->
-      _.extend super, s: @model.get 'severity'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/layout.coffee b/server/sonar-web/src/main/coffee/apps/issues/layout.coffee
deleted file mode 100644 (file)
index ac81d54..0000000
+++ /dev/null
@@ -1,69 +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'
-], ->
-
-  $ = jQuery
-
-
-  class extends Marionette.Layout
-    template: Templates['issues-layout']
-
-
-    regions:
-      filtersRegion: '.search-navigator-filters'
-      facetsRegion: '.search-navigator-facets'
-      workspaceHeaderRegion: '.search-navigator-workspace-header'
-      workspaceListRegion: '.search-navigator-workspace-list'
-      workspaceComponentViewerRegion: '.issues-workspace-component-viewer'
-      workspaceHomeRegion: '.issues-workspace-home'
-
-
-    onRender: ->
-      @$(@filtersRegion.el).addClass('hidden') if @options.app.state.get('isContext')
-      $('.search-navigator').addClass 'sticky'
-      top = $('.search-navigator').offset().top
-      @$('.search-navigator-workspace-header').css top: top
-      @$('.search-navigator-side').css({ top: top }).isolatedScroll()
-
-
-    showSpinner: (region) ->
-      @[region].show new Marionette.ItemView
-        template: _.template('<i class="spinner"></i>')
-
-
-    showComponentViewer: ->
-      @scroll = $(window).scrollTop()
-      $('.issues').addClass 'issues-extended-view'
-
-
-    hideComponentViewer: ->
-      $('.issues').removeClass 'issues-extended-view'
-      $(window).scrollTop @scroll if @scroll?
-
-
-    showHomePage: ->
-      $('.issues').addClass 'issues-home-view'
-
-
-    hideHomePage: ->
-      $('.issues').removeClass 'issues-home-view'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/facet.coffee
deleted file mode 100644 (file)
index 96bc2d8..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 ->
-
-  class extends Backbone.Model
-    idAttribute: 'property'
-
-
-    defaults:
-      enabled: false
-
-
-    getValues: ->
-      @get('values') || []
-
-
-    toggle: ->
-      enabled = @get 'enabled'
-      @set enabled: !enabled
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/facets.coffee
deleted file mode 100644 (file)
index 3f6a957..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 [
-  './facet'
-], (
-  Facet
-) ->
-
-  class extends Backbone.Collection
-    model: Facet
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/filter.coffee
deleted file mode 100644 (file)
index 9759bc3..0000000
+++ /dev/null
@@ -1,30 +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
-
-    url: ->
-      "/api/issue_filters/show/#{@id}"
-
-
-    parse: (r) ->
-      if r.filter? then r.filter else r
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/filters.coffee
deleted file mode 100644 (file)
index b002dba..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 [
-  './filter'
-], (
-  Filter
-) ->
-
-  class extends Backbone.Collection
-    model: Filter
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/issues.coffee
deleted file mode 100644 (file)
index d98b263..0000000
+++ /dev/null
@@ -1,80 +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 [
-  'components/issue/models/issue'
-], (
-  Issue
-) ->
-
-  class extends Backbone.Collection
-    model: Issue
-
-    url: ->
-      "#{baseUrl}/api/issues/search"
-
-
-    # Used to parse /api/issues/search response
-    parseIssues: (r) ->
-      find = (source, key, keyField) ->
-        searchDict = {}
-        searchDict[keyField || 'key'] = key
-        _.findWhere(source, searchDict) || key
-
-      r.issues.map (issue, index) ->
-        component = find r.components, issue.component
-        project = find r.projects, issue.project
-        subProject = find r.components, issue.subProject
-        rule = find r.rules, issue.rule
-        assignee = find r.users, issue.assignee, 'login'
-
-        _.extend issue,
-          index: index
-
-        if component
-          _.extend issue,
-            componentUuid: component.uuid
-            componentLongName: component.longName
-            componentQualifier: component.qualifier
-
-        if project
-          _.extend issue,
-            projectLongName: project.longName
-            projectUuid: project.uuid
-
-        if subProject
-          _.extend issue,
-            subProjectLongName: subProject.longName
-            subProjectUuid: subProject.uuid
-
-        if rule
-          _.extend issue,
-            ruleName: rule.name
-
-        if assignee
-          _.extend issue,
-            assigneeEmail: assignee.email
-
-        issue
-
-
-    setIndex: ->
-      @forEach (issue, index) ->
-        issue.set index: index
diff --git a/server/sonar-web/src/main/coffee/apps/issues/models/state.coffee b/server/sonar-web/src/main/coffee/apps/issues/models/state.coffee
deleted file mode 100644 (file)
index b0588d2..0000000
+++ /dev/null
@@ -1,47 +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 [
-  'components/navigator/models/state'
-], (State) ->
-  class extends State
-
-    defaults:
-      page: 1
-      maxResultsReached: false
-      query: {}
-      facets: ['severities', 'resolutions']
-      isContext: false
-
-      allFacets: ['issues', 'severities', 'resolutions', 'statuses', 'createdAt', 'rules', 'tags', 'projectUuids',
-                  'moduleUuids', 'directories', 'fileUuids', 'assignees', 'reporters', 'authors', 'languages',
-                  'actionPlans'],
-      facetsFromServer: ['severities', 'statuses', 'resolutions', 'actionPlans', 'projectUuids', 'directories', 'rules',
-                         'moduleUuids', 'tags', 'assignees', 'reporters', 'authors', 'fileUuids', 'languages',
-                         'createdAt'],
-      transform: {
-        'resolved': 'resolutions'
-        'assigned': 'assignees'
-        'planned': 'actionPlans'
-        'createdBefore': 'createdAt'
-        'createdAfter': 'createdAt'
-        'createdInLast': 'createdAt'
-      }
-
diff --git a/server/sonar-web/src/main/coffee/apps/issues/router.coffee b/server/sonar-web/src/main/coffee/apps/issues/router.coffee
deleted file mode 100644 (file)
index ca22054..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.
-#
-
-define [
-  'components/navigator/router'
-], (
-  Router
-) ->
-
-  class extends Router
-    routes:
-      '': 'home'
-      ':query': 'index'
-
-
-    initialize: (options) ->
-      super
-      @listenTo options.app.state, 'change:filter', @updateRoute
-
-
-    home: ->
-      if @options.app.state.get 'isContext'
-        @navigate 'resolved=false', { trigger: true, replace: true }
-      else
-        @options.app.controller.showHomePage()
-
-
-    index: (query) ->
-      query = @options.app.controller.parseQuery query
-      if query.id?
-        filter = @options.app.filters.get query.id
-        delete query.id
-        filter.fetch().done =>
-          if Object.keys(query).length > 0
-            @options.app.controller.applyFilter filter, true
-            @options.app.state.setQuery query
-            @options.app.state.set changed: true
-          else
-            @options.app.controller.applyFilter filter
-      else
-        @options.app.state.setQuery query
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/_issues-filter-name.hbs
deleted file mode 100644 (file)
index 374d15a..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{{#if filter.name}}
-  {{filter.name}}
-  <span class="note nowrap">
-    {{#unless filter.shared}}
-      [{{t 'issue_filter.private'}}]
-    {{else}}
-      {{#eq filter.user currentUser}}
-        [{{t 'issue_filter.shared_with_all_users'}}]
-      {{else}}
-        {{#if filter.user}}
-          [{{t 'issue_filter.shared'}}]
-        {{/if}}
-      {{/eq}}
-    {{/unless}}
-  </span>
-{{else}}
-  {{t 'issues'}}
-{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/_issues-facet-header.hbs
deleted file mode 100644 (file)
index 3b57c60..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<a class="search-navigator-facet-header js-facet-toggle">
-  <i class="icon-checkbox {{#if enabled}}icon-checkbox-checked{{/if}}"></i>
-  {{t 'issues.facet' property}}
-</a>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-action-plan-facet.hbs
deleted file mode 100644 (file)
index 20958a5..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    {{#eq val ''}}
-      {{! unplanned }}
-      <a class="facet search-navigator-facet js-facet" data-unplanned title="{{t 'issue.unplanned'}}">
-        <span class="facet-name">{{t 'issue.unplanned'}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{else}}
-      <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{label}}">
-        <span class="facet-name">{{label}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{/eq}}
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-assignee-facet.hbs
deleted file mode 100644 (file)
index 9654cf0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#notNull myIssues}}
-    <a class="facet search-navigator-facet js-facet" data-value="__me__" title="{{t 'me'}}">
-      <span class="facet-name">{{t 'me'}}</span>
-      <span class="facet-stat">{{myIssues}}</span>
-    </a>
-    <hr>
-  {{/notNull}}
-
-  {{#each values}}
-    {{#eq val ''}}
-    {{! unassigned }}
-      <a class="facet search-navigator-facet js-facet" data-unassigned title="{{t 'unassigned'}}">
-        <span class="facet-name">{{t 'unassigned'}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{else}}
-      <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{label}}">
-        <span class="facet-name">{{label}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{/eq}}
-  {{/each}}
-
-  <div class="search-navigator-facet-custom-value">
-    <input type="hidden" class="js-custom-value">
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-base-facet.hbs
deleted file mode 100644 (file)
index f5b0f44..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
-      <span class="facet-name">{{default label val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
-    </a>
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-context-facet.hbs
deleted file mode 100644 (file)
index 9f981c0..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="search-navigator-facet-query">
-  Issues of &nbsp;&nbsp; {{qualifierIcon state.contextComponentQualifier}}&nbsp;{{state.contextComponentName}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-creation-date-facet.hbs
deleted file mode 100644 (file)
index 4cc52cc..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-{{> '_issues-facet-header'}}
-
-{{#if createdAt}}
-  <input type="hidden" name="createdAt">
-  <div class="search-navigator-facet-container">
-    {{dt createdAt}} ({{fromNow createdAt}})
-  </div>
-{{else}}
-  <div class="search-navigator-facet-container">
-    <div class="js-barchart" data-height="75" {{#if periodEnd}}data-end-date="{{periodEnd}}"{{/if}}></div>
-    <div class="search-navigator-date-facet-selection">
-      <a class="js-select-period-start search-navigator-date-facet-selection-dropdown-left">
-        {{#if periodStart}}{{d periodStart}}{{else}}Past{{/if}}&nbsp;<i class="icon-dropdown"></i>
-      </a>
-      <a class="js-select-period-end search-navigator-date-facet-selection-dropdown-right">
-        {{#if periodEnd}}{{d periodEnd}}{{else}}Now{{/if}}&nbsp;<i class="icon-dropdown"></i>
-      </a>
-      <input class="js-period-start search-navigator-date-facet-selection-input-left"
-             type="text" value="{{#if periodStart}}{{ds periodStart}}{{/if}}" name="createdAfter">
-      <input class="js-period-end search-navigator-date-facet-selection-input-right"
-             type="text" value="{{#if periodEnd}}{{ds periodEnd}}{{/if}}" name="createdBefore">
-    </div>
-
-    <div class="spacer-top">
-      <span class="spacer-right">{{t 'issues.facet.createdAt.or'}}</span>
-      <a class="js-all spacer-right" href="#">{{t 'issues.facet.createdAt.all'}}</a>
-      <a class="js-last-week spacer-right {{#eq createdInLast '1w'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_week'}}</a>
-      <a class="js-last-month spacer-right {{#eq createdInLast '1m'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_month'}}</a>
-      <a class="js-last-year {{#eq createdInLast '1y'}}active-link{{/eq}}" href="#">{{t 'issues.facet.createdAt.last_year'}}</a>
-    </div>
-  </div>
-{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-custom-values-facet.hbs
deleted file mode 100644 (file)
index c7d8a08..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{#if extra}}({{extra}}) {{/if}}{{default label val}}">
-      <span class="facet-name">{{default label val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
-    </a>
-  {{/each}}
-
-  <div class="search-navigator-facet-custom-value">
-    <input type="hidden" class="js-custom-value">
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-file-facet.hbs
deleted file mode 100644 (file)
index 530959b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list search-navigator-facet-list-align-right">
-  {{#each values}}
-    <a class="facet search-navigator-facet js-facet" data-value="{{val}}" title="{{default label val}}">
-      <span class="facet-name">{{default label val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
-    </a>
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-issue-key-facet.hbs
deleted file mode 100644 (file)
index d68931e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-container">
-  <div class="facet search-navigator-facet active" style="cursor: default;">
-    <span class="facet-name">{{issues}}</span>
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-resolution-facet.hbs
deleted file mode 100644 (file)
index 08c1b25..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    {{#eq val ''}}
-    {{! unresolved }}
-      <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-unresolved
-         title="{{t 'issue.unresolved.description'}}" data-toggle="tooltip" data-placement="right">
-        <span class="facet-name">{{t 'unresolved'}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{else}}
-      <a class="facet search-navigator-facet search-navigator-facet-half js-facet" data-value="{{val}}"
-         title="{{t 'issue.resolution' val 'description'}}" data-toggle="tooltip" data-placement="right">
-        <span class="facet-name">{{t 'issue.resolution' val}}</span>
-        <span class="facet-stat">{{numberShort count}}</span>
-      </a>
-    {{/eq}}
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-severity-facet.hbs
deleted file mode 100644 (file)
index 96a6470..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    <a class="facet search-navigator-facet search-navigator-facet-half js-facet"
-       data-value="{{val}}" title="{{t 'severity' val 'description'}}" data-toggle="tooltip" data-placement="right">
-      <span class="facet-name">{{severityIcon val}} {{t 'severity' val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
-    </a>
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/facets/issues-status-facet.hbs
deleted file mode 100644 (file)
index 33e556a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{{> '_issues-facet-header'}}
-
-<div class="search-navigator-facet-list">
-  {{#each values}}
-    <a class="facet search-navigator-facet search-navigator-facet-half js-facet"
-       data-value="{{val}}" title="{{t 'issue.status' val 'description'}}" data-toggle="tooltip" data-placement="right">
-      <span class="facet-name">{{statusIcon val}} {{t 'issue.status' val}}</span>
-      <span class="facet-stat">{{numberShort count}}</span>
-    </a>
-  {{/each}}
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-filters.hbs
deleted file mode 100644 (file)
index a197f53..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<h1 class="page-title dropdown">
-  {{#if state.canManageFilters}}
-    <a class="search-navigator-filters-show-list dropdown-toggle" data-toggle="dropdown">
-      <i class="icon-list"></i><span class="issues-filters-name">{{> '_issues-filter-name'}}</span>
-    </a>
-    <ul class="dropdown-menu">
-      {{#each items}}
-        <li>
-          <a class="search-navigator-filters-button search-navigator-filters-filter js-filter" data-id="{{id}}">
-            {{name}}
-          </a>
-        </li>
-      {{/each}}
-      {{#notEmpty items}}
-        <li class="divider"></li>
-      {{/notEmpty}}
-      <li>
-        <a class="search-navigator-filters-manage" href="{{link '/issues/manage'}}">{{t 'manage'}}</a>
-      </li>
-    </ul>
-    {{#if filter.description}}
-      <div class="search-navigator-filters-description">{{filter.description}}</div>
-    {{/if}}
-  {{else}}
-    <span class="search-navigator-filters-name">{{t 'issues'}}</span>
-  {{/if}}
-</h1>
-
-<div class="page-actions">
-  <div class="button-group">
-    {{#if state.canManageFilters}}
-      {{#if filter.canModify}}
-        {{#if state.changed}}
-          <button class="js-filter-save" id="issues-filter-save">{{t 'save'}}</button>
-        {{/if}}
-      {{/if}}
-
-      {{#unless filter.id}}
-        <button class="js-filter-save-as" id="issues-filter-save-as">{{t 'save_as'}}</button>
-      {{/unless}}
-
-      {{#if filter.id}}
-        {{#unless state.changed}}
-          <button class="js-filter-copy" id="issues-filter-copy">{{t 'copy'}}</button>
-        {{/unless}}
-      {{/if}}
-
-      {{#if filter.canModify}}
-        {{#if filter.id}}
-          <button class="js-filter-edit" id="issues-filter-edit">{{t 'edit'}}</button>
-        {{/if}}
-      {{/if}}
-    {{/if}}
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter-form.hbs
deleted file mode 100644 (file)
index 0151574..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<h6>{{t 'issue.filter_similar_issues'}}</h6>
-
-<div class="issue-action-options">
-  <a href="#" class="issue-action-option" data-property="severities" data-value="{{s}}">
-    {{severityIcon severity}}&nbsp;{{t 'severity' severity}}
-  </a>
-
-  <a href="#" class="issue-action-option" data-property="statuses" data-value="{{status}}">
-    {{statusIcon status}}&nbsp;{{t 'issue.status' status}}
-  </a>
-
-  {{#if resolution}}
-    <a href="#" class="issue-action-option" data-property="resolutions" data-value="{{resolution}}">
-      {{t 'issue.resolution' resolution}}
-    </a>
-  {{else}}
-    <a href="#" class="issue-action-option" data-property="resolved" data-value="false">
-      {{t 'unresolved'}}
-    </a>
-  {{/if}}
-
-  {{#if assignee}}
-    <a href="#" class="issue-action-option" data-property="assignees" data-value="{{assignee}}">
-      {{t 'assigned_to'}} {{assigneeName}}
-    </a>
-  {{else}}
-    <a href="#" class="issue-action-option" data-property="assigned" data-value="false">
-      {{t 'unassigned'}}
-    </a>
-  {{/if}}
-
-  {{#if actionPlan}}
-    <a href="#" class="issue-action-option" data-property="actionPlans" data-value="{{actionPlan}}">
-      {{t 'issue.planned_for'}} {{actionPlanName}}
-    </a>
-  {{else}}
-    <a href="#" class="issue-action-option" data-property="planned" data-value="false">
-      {{t 'issue.unplanned'}}
-    </a>
-  {{/if}}
-
-  <hr>
-
-  <a href="#" class="issue-action-option" data-property="rules" data-value="{{rule}}">
-    {{limitString ruleName}}
-  </a>
-
-  {{#each tags}}
-    <a href="#" class="issue-action-option" data-property="tags" data-value="{{this}}">
-      <i class="icon-tags icon-half-transparent"></i>&nbsp;{{this}}
-    </a>
-  {{/each}}
-
-  <hr>
-
-  <a href="#" class="issue-action-option" data-property="projectUuids" data-value="{{projectUuid}}">
-    {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
-  </a>
-
-  {{#if subProject}}
-    <a href="#" class="issue-action-option" data-property="moduleUuids" data-value="{{subProjectUuid}}">
-      {{qualifierIcon 'BRC'}}&nbsp;{{subProjectLongName}}
-    </a>
-  {{/if}}
-
-  <a href="#" class="issue-action-option" data-property="fileUuids" data-value="{{componentUuid}}">
-    {{qualifierIcon componentQualifier}}&nbsp;{{fileFromPath componentLongName}}
-  </a>
-</div>
-
-<div class="bubble-popup-arrow"></div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-issue-filter.hbs
deleted file mode 100644 (file)
index 875bc3b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<div class="issue-meta">
-  <a class="issue-action issue-action-with-options js-issue-filter" href="#">
-    <i class="icon-filter icon-half-transparent"></i>&nbsp;<i class="icon-dropdown"></i>
-  </a>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-layout.hbs
deleted file mode 100644 (file)
index 5a61d1f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="search-navigator-side">
-  <div class="search-navigator-filters"></div>
-  <div class="search-navigator-facets"></div>
-</div>
-
-<div class="search-navigator-workspace">
-  <div class="search-navigator-workspace-header"></div>
-  <div class="search-navigator-workspace-list"></div>
-  <div class="issues-workspace-component-viewer"></div>
-  <div class="issues-workspace-home"></div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-header.hbs
deleted file mode 100644 (file)
index 4b4958f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<div class="issues-header-component nowrap">
-  {{#if state.component}}
-    <a class="js-back">{{t 'issues.return_to_list'}}</a>&nbsp;&nbsp;&nbsp;
-
-    {{#with state.component}}
-      {{qualifierIcon 'TRK'}}&nbsp;<a href="{{dashboardUrl project}}" title="{{projectName}}">{{projectName}}</a>
-      &nbsp;&nbsp;
-      {{qualifierIcon qualifier}}&nbsp;<a href="{{dashboardUrl key}}" title="{{name}}">{{name}}</a>
-    {{/with}}
-  {{else}}
-    &nbsp;
-  {{/if}}
-</div>
-
-
-<div class="search-navigator-header-actions">
-  {{#notNull state.total}}
-    <div class="search-navigator-header-pagination">
-      {{#gt state.total 0}}
-        <a class="js-prev icon-prev" title="{{t 'paging_previous'}}"></a>
-        <span class="current">{{sum state.selectedIndex 1}} / <span id="issues-total">{{state.total}}</span></span>
-        <a class="js-next icon-next" title="{{t 'paging_next'}}"></a>
-      {{else}}
-        <span class="current">0 / <span id="issues-total">0</span></span>
-      {{/gt}}
-    </div>
-  {{/notNull}}
-
-
-  <div class="search-navigator-header-buttons button-group">
-    <button id="issues-reload" class="js-reload">{{t 'reload'}}</button>
-    <button class="js-new-search" id="issues-new-search">{{t 'issue_filter.new_search'}}</button>
-    {{#if state.canBulkChange}}
-      <button id="issues-bulk-change" class="js-bulk-change">{{t 'bulk_change'}}</button>
-    {{/if}}
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-home.hbs
deleted file mode 100644 (file)
index 3889dcd..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<div class="spacer-top spacer-bottom">
-  <div class="columns">
-    <div class="column-half {{#unless user}}column-one{{/unless}}">
-      <h3 class="text-center">{{t 'issues.home.recent_issues'}}</h3>
-      <p class="note text-center">({{t 'issues.home.over_last_week'}})</p>
-
-      <div class="spacer-top text-center js-barchart" data-height="75" data-width="300"></div>
-      <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.projects'}}</h4>
-      <table class="data zebra spacer-top">
-        {{#each projects}}
-          <tr>
-            <td>{{qualifierIcon 'TRK'}}&nbsp;<a href="{{issuesHomeLink 'projectUuids' val}}">{{label}}</a></td>
-            <td class="thin text-right">+{{numberShort count}}</td>
-          </tr>
-        {{/each}}
-      </table>
-      <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.authors'}}</h4>
-      <table class="data zebra spacer-top">
-        {{#each authors}}
-          <tr>
-            <td><a href="{{issuesHomeLink 'authors' val}}">{{val}}</a></td>
-            <td class="thin text-right">+{{numberShort count}}</td>
-          </tr>
-        {{/each}}
-      </table>
-      <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.tags'}}</h4>
-      <ul class="list-inline">
-        {{#each tags}}
-          <li><a class="link-no-underline" style="font-size: {{size}}px;" data-toggle="tooltip" data-placement="bottom"
-                 href="{{issuesHomeLink 'tags' val}}"
-                 title="+{{numberShort count}}">{{val}}</a></li>
-        {{/each}}
-      </ul>
-    </div>
-
-    {{#if user}}
-      <div class="column-half">
-        <h3 class="text-center">{{t 'issues.home.my_recent_issues'}}</h3>
-        <p class="note text-center">({{t 'issues.home.over_last_week'}})</p>
-
-        <div class="spacer-top text-center js-my-barchart" data-height="75" data-width="300"></div>
-        {{#notEmpty myProjects}}
-          <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.projects'}}</h4>
-          <table class="data zebra spacer-top">
-            {{#each myProjects}}
-              <tr>
-                <td>{{qualifierIcon 'TRK'}}&nbsp;<a href="{{myIssuesHomeLink 'projectUuids' val}}">{{label}}</a></td>
-                <td class="thin text-right">+{{numberShort count}}</td>
-              </tr>
-            {{/each}}
-          </table>
-        {{/notEmpty}}
-        {{#notEmpty myTags}}
-          <h4 class="spacer-top spacer-bottom text-center">{{t 'issues.home.tags'}}</h4>
-          <ul class="list-inline">
-            {{#each myTags}}
-              <li><a class="link-no-underline" style="font-size: {{size}}px;" data-toggle="tooltip" data-placement="bottom"
-                     href="{{myIssuesHomeLink 'tags' val}}"
-                     title="+{{numberShort count}}">{{val}}</a></li>
-            {{/each}}
-          </ul>
-        {{/notEmpty}}
-        {{#notEmpty filters}}
-          <h3 class="spacer-bottom text-center" style="padding-top: 12px; margin-top: 20px; border-top: 1px solid #efefef;">
-            {{t 'issues.home.my_filters'}}</h3>
-          <ul class="list-inline">
-            {{#each filters}}
-              <li>
-                <a href="{{issueFilterHomeLink id}}">{{name}}</a>
-              </li>
-            {{/each}}
-          </ul>
-        {{/notEmpty}}
-      </div>
-    {{/if}}
-  </div>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list-component.hbs
deleted file mode 100644 (file)
index d5d0b43..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="issues-workspace-list-component">
-  <a class="issues-workspace-list-component-part" href="{{dashboardUrl project}}">
-    {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
-  </a>
-  {{#if subProject}}
-    <a class="issues-workspace-list-component-part" href="{{dashboardUrl subProject}}">
-      {{qualifierIcon 'TRK'}}&nbsp;{{subProjectLongName}}
-    </a>
-  {{/if}}
-  <a class="issues-workspace-list-component-part" href="{{dashboardUrl component}}">
-    {{qualifierIcon componentQualifier}}&nbsp;{{componentLongName}}
-  </a>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs b/server/sonar-web/src/main/coffee/apps/issues/templates/issues-workspace-list.hbs
deleted file mode 100644 (file)
index 37421cb..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<div class="js-list"></div>
-
-<div class="search-navigator-workspace-list-more js-more">
-  <i class="spinner"></i>
-</div>
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-header-view.coffee
deleted file mode 100644 (file)
index 7672d1a..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 [
-  'components/navigator/workspace-header-view'
-  './templates'
-], (
-  WorkspaceHeaderView
-) ->
-
-  $ = jQuery
-
-
-  class extends WorkspaceHeaderView
-    template: Templates['issues-workspace-header']
-
-
-    events: ->
-      _.extend super,
-        'click .js-back': 'returnToList'
-        'click .js-new-search': 'newSearch'
-
-
-    initialize: ->
-      super
-      @_onBulkIssues = window.onBulkIssues
-      window.onBulkIssues = =>
-        $('#modal').dialog 'close'
-        @options.app.controller.fetchList()
-
-
-    onClose: ->
-      window.onBulkIssues = @_onBulkIssues
-
-
-    returnToList: ->
-      @options.app.controller.closeComponentViewer()
-
-
-    newSearch: ->
-      @options.app.controller.newSearch()
-
-
-    bulkChange: ->
-      query = @options.app.controller.getQuery '&', true
-      url = "#{baseUrl}/issues/bulk_change_form?#{query}"
-      openModalWindow url, {}
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-home-view.coffee
deleted file mode 100644 (file)
index 6bf9c15..0000000
+++ /dev/null
@@ -1,149 +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 [
-  'widgets/issue-filter/widget'
-  './templates'
-], (IssueFilter) ->
-
-  $ = jQuery
-
-
-  Handlebars.registerHelper 'issuesHomeLink', (property, value) ->
-    "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|#{property}=#{encodeURIComponent value}"
-
-  Handlebars.registerHelper 'myIssuesHomeLink', (property, value) ->
-    "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|assignees=__me__|#{property}=#{encodeURIComponent value}"
-
-  Handlebars.registerHelper 'issueFilterHomeLink', (id) ->
-    "#{baseUrl}/issues/search#id=#{id}"
-
-
-  class extends Marionette.ItemView
-    template: Templates['issues-workspace-home']
-
-
-    modelEvents:
-      'change': 'render'
-
-
-    events:
-      'click .js-barchart rect': 'selectBar'
-      'click .js-my-barchart rect': 'selectMyBar'
-
-
-    initialize: ->
-      @model = new Backbone.Model
-      @requestIssues()
-      @requestMyIssues()
-
-
-    _getProjects: (r) ->
-      projectFacet = _.findWhere r.facets, property: 'projectUuids'
-      if projectFacet?
-        values = _.head projectFacet.values, 3
-        values.forEach (v) =>
-          project = _.findWhere r.projects, uuid: v.val
-          v.label = project.longName
-        values
-
-
-    _getAuthors: (r) ->
-      authorFacet = _.findWhere r.facets, property: 'authors'
-      if authorFacet?
-        values = _.head authorFacet.values, 3
-        values
-
-
-    _getTags: (r) ->
-      MIN_SIZE = 10
-      MAX_SIZE = 24
-      tagFacet = _.findWhere r.facets, property: 'tags'
-      if tagFacet?
-        values = _.head tagFacet.values, 10
-        minCount = _.min(values, (v) -> v.count).count
-        maxCount = _.max(values, (v) -> v.count).count
-        scale = d3.scale.linear().domain([minCount, maxCount]).range([MIN_SIZE, MAX_SIZE])
-        values.forEach (v) =>
-          v.size = scale v.count
-        values
-
-
-    requestIssues: ->
-      url = "#{baseUrl}/api/issues/search"
-      options =
-        resolved: false
-        createdInLast: '1w'
-        ps: 1
-        facets: 'createdAt,projectUuids,authors,tags'
-      $.get(url, options).done (r) =>
-        @model.set
-          createdAt: _.findWhere(r.facets, property: 'createdAt')?.values
-          projects: @_getProjects r
-          authors: @_getAuthors r
-          tags: @_getTags r
-
-
-    requestMyIssues: ->
-      url = "#{baseUrl}/api/issues/search"
-      options =
-        resolved: false
-        createdInLast: '1w'
-        assignees: '__me__'
-        ps: 1
-        facets: 'createdAt,projectUuids,authors,tags'
-      $.get(url, options).done (r) =>
-        @model.set
-          myCreatedAt: _.findWhere(r.facets, property: 'createdAt')?.values
-          myProjects: @_getProjects r
-          myTags: @_getTags r
-
-
-    onRender: ->
-      values = @model.get 'createdAt'
-      myValues = @model.get 'myCreatedAt'
-      @$('.js-barchart').barchart values if values?
-      @$('.js-my-barchart').barchart myValues if myValues?
-      @$('[data-toggle="tooltip"]').tooltip container: 'body'
-
-
-    selectBar: (e) ->
-      periodStart = $(e.currentTarget).data 'period-start'
-      periodEnd = $(e.currentTarget).data 'period-end'
-      @options.app.state.setQuery
-        resolved: false
-        createdAfter: periodStart
-        createdBefore: periodEnd
-
-
-    selectMyBar: (e) ->
-      periodStart = $(e.currentTarget).data 'period-start'
-      periodEnd = $(e.currentTarget).data 'period-end'
-      @options.app.state.setQuery
-        resolved: false
-        assignees: '__me__'
-        createdAfter: periodStart
-        createdBefore: periodEnd
-
-
-    serializeData: ->
-      _.extend super,
-        user: window.SS.user
-        filters: _.sortBy @options.app.filters.toJSON(), 'name'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-empty-view.coffee
deleted file mode 100644 (file)
index 23bba0e..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 ->
-
-  class extends Marionette.ItemView
-    className: 'search-navigator-no-results'
-
-
-    template: ->
-      t 'issue_filter.no_issues'
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-item-view.coffee
deleted file mode 100644 (file)
index 62b223f..0000000
+++ /dev/null
@@ -1,118 +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 [
-  'components/issue/issue-view'
-  './issue-filter-view'
-  './templates'
-], (
-  IssueView
-  IssueFilterView
-) ->
-
-  $ = jQuery
-
-  SHOULD_NULL =
-    any: ['issues']
-    resolutions: ['resolved']
-    resolved: ['resolutions']
-    assignees: ['assigned']
-    assigned: ['assignees']
-    actionPlans: ['planned']
-    planned: ['actionPlans']
-
-  class extends IssueView
-    filterTemplate: Templates['issues-issue-filter']
-
-    events: ->
-      _.extend super,
-        'click': 'selectCurrent'
-        'dblclick': 'openComponentViewer'
-        'click .js-issue-navigate': 'openComponentViewer'
-        'click .js-issue-filter': 'onIssueFilterClick'
-
-
-    initialize: (options) ->
-      super
-      @listenTo options.app.state, 'change:selectedIndex', @select
-
-
-    onRender: ->
-      super
-      @select()
-      @addFilterSelect()
-      @$el.addClass 'issue-navigate-right'
-
-
-    onIssueFilterClick: (e) ->
-      e.preventDefault()
-      e.stopPropagation()
-      $('body').click()
-      @popup = new IssueFilterView
-        triggerEl: $(e.currentTarget)
-        bottomRight: true
-        model: @model
-      @popup.on 'select', (property, value) =>
-        obj = {}
-        obj[property] = '' + value
-        SHOULD_NULL.any.forEach (p) -> obj[p] = null
-        if SHOULD_NULL[property]?
-          SHOULD_NULL[property].forEach (p) -> obj[p] = null
-        @options.app.state.updateFilter obj
-        @popup.close()
-      @popup.render()
-
-
-
-    addFilterSelect: ->
-      @$('.issue-table-meta-cell-first').find('.issue-meta-list').append @filterTemplate @model.toJSON()
-
-
-    select: ->
-      selected = @model.get('index') == @options.app.state.get 'selectedIndex'
-      @$el.toggleClass 'selected', selected
-
-
-    selectCurrent: ->
-      @options.app.state.set selectedIndex: @model.get('index')
-
-
-    resetIssue: (options) ->
-      key = @model.get 'key'
-      componentUuid = @model.get 'componentUuid'
-      index = @model.get 'index'
-      @model.clear silent: true
-      @model.set { key: key, componentUuid: componentUuid, index: index }, { silent: true }
-      @model.fetch(options)
-      .done =>
-        @trigger 'reset'
-
-
-    openComponentViewer: ->
-      @options.app.state.set selectedIndex: @model.get('index')
-      if @options.app.state.has 'component'
-        @options.app.controller.closeComponentViewer()
-      else
-        @options.app.controller.showComponentViewer @model
-
-
-    serializeData: ->
-      _.extend super,
-        showComponent: true
diff --git a/server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee b/server/sonar-web/src/main/coffee/apps/issues/workspace-list-view.coffee
deleted file mode 100644 (file)
index 889b033..0000000
+++ /dev/null
@@ -1,103 +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 [
-  'components/navigator/workspace-list-view'
-  './workspace-list-item-view'
-  './workspace-list-empty-view'
-  './templates'
-], (
-  WorkspaceListView
-  IssueView
-  EmptyView
-) ->
-
-  $ = jQuery
-
-  COMPONENT_HEIGHT = 29
-  BOTTOM_OFFSET = 10
-
-
-  class extends WorkspaceListView
-    template: Templates['issues-workspace-list']
-    componentTemplate: Templates['issues-workspace-list-component']
-    itemView: IssueView
-    itemViewContainer: '.js-list'
-    emptyView: EmptyView
-
-
-    bindShortcuts: ->
-      doAction = (action) =>
-        selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
-        return unless selectedIssue?
-        selectedIssueView = @children.findByModel selectedIssue
-        selectedIssueView.$(".js-issue-#{action}").click()
-
-      super
-
-      key 'right', 'list', =>
-        selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
-        @options.app.controller.showComponentViewer selectedIssue
-        return false
-
-      key 'f', 'list', -> doAction 'transition'
-      key 'a', 'list', -> doAction 'assign'
-      key 'm', 'list', -> doAction 'assign-to-me'
-      key 'p', 'list', -> doAction 'plan'
-      key 'i', 'list', -> doAction 'set-severity'
-      key 'c', 'list', -> doAction 'comment'
-      key 't', 'list', -> doAction 'edit-tags'
-
-
-    scrollTo: ->
-      selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
-      return unless selectedIssue?
-      selectedIssueView = @children.findByModel selectedIssue
-      parentTopOffset = @$el.offset().top
-      viewTop = selectedIssueView.$el.offset().top - parentTopOffset
-      if selectedIssueView.$el.prev().is('.issues-workspace-list-component')
-        viewTop -= COMPONENT_HEIGHT
-      viewBottom = selectedIssueView.$el.offset().top + selectedIssueView.$el.outerHeight() + BOTTOM_OFFSET
-      windowTop = $(window).scrollTop()
-      windowBottom = windowTop + $(window).height()
-      if viewTop < windowTop
-        $(window).scrollTop viewTop
-      if viewBottom > windowBottom
-        $(window).scrollTop $(window).scrollTop() - windowBottom + viewBottom
-
-
-    appendHtml: (compositeView, itemView, index) ->
-      $container = this.getItemViewContainer compositeView
-      model = @collection.at(index)
-      if model?
-        prev = @collection.at(index - 1)
-        putComponent = !prev?
-        if prev?
-          fullComponent = [model.get('project'), model.get('component')].join ' '
-          fullPrevComponent = [prev.get('project'), prev.get('component')].join ' '
-          putComponent = true unless fullComponent == fullPrevComponent
-        if putComponent
-          $container.append @componentTemplate model.toJSON()
-      $container.append itemView.el
-
-
-    closeChildren: ->
-      super
-      @$('.issues-workspace-list-component').remove()
diff --git a/server/sonar-web/src/main/js/apps/issues/app-context.js b/server/sonar-web/src/main/js/apps/issues/app-context.js
new file mode 100644 (file)
index 0000000..bcef084
--- /dev/null
@@ -0,0 +1,105 @@
+define([
+  './models/state',
+  './layout',
+  './models/issues',
+  'components/navigator/models/facets',
+  './models/filters',
+  './controller',
+  './router',
+  './workspace-list-view',
+  './workspace-header-view',
+  './facets-view'
+], function (State, Layout, Issues, Facets, Filters, Controller, Router, WorkspaceListView, WorkspaceHeaderView,
+             FacetsView) {
+
+  var $ = jQuery,
+      App = new Marionette.Application();
+
+  App.getContextQuery = function () {
+    return { componentUuids: window.config.resource };
+  };
+
+  App.getRestrictedFacets = function () {
+    return {
+      'TRK': ['projectUuids'],
+      'BRC': ['projectUuids'],
+      'DIR': ['projectUuids', 'moduleUuids', 'directories'],
+      'DEV': ['authors'],
+      'DEV_PRJ': ['projectUuids', 'authors']
+    };
+  };
+
+  App.updateContextFacets = function () {
+    var facets = this.state.get('facets'),
+        allFacets = this.state.get('allFacets'),
+        facetsFromServer = this.state.get('facetsFromServer');
+    return this.state.set({
+      facets: facets,
+      allFacets: _.difference(allFacets, this.getRestrictedFacets()[window.config.resourceQualifier]),
+      facetsFromServer: _.difference(facetsFromServer, this.getRestrictedFacets()[window.config.resourceQualifier])
+    });
+  };
+
+  App.addInitializer(function () {
+    this.state = new State({
+      isContext: true,
+      contextQuery: this.getContextQuery(),
+      contextComponentUuid: window.config.resource,
+      contextComponentName: window.config.resourceName,
+      contextComponentQualifier: window.config.resourceQualifier
+    });
+    this.updateContextFacets();
+    this.list = new Issues();
+    this.facets = new Facets();
+    this.filters = new Filters();
+  });
+
+  App.addInitializer(function () {
+    this.layout = new Layout({ app: this });
+    $('.issues').empty().append(this.layout.render().el);
+    $('#footer').addClass('search-navigator-footer');
+  });
+
+  App.addInitializer(function () {
+    this.controller = new Controller({ app: this });
+  });
+
+  App.addInitializer(function () {
+    this.issuesView = new WorkspaceListView({
+      app: this,
+      collection: this.list
+    });
+    this.layout.workspaceListRegion.show(this.issuesView);
+    this.issuesView.bindScrollEvents();
+  });
+
+  App.addInitializer(function () {
+    this.workspaceHeaderView = new WorkspaceHeaderView({
+      app: this,
+      collection: this.list
+    });
+    this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView);
+  });
+
+  App.addInitializer(function () {
+    this.facetsView = new FacetsView({
+      app: this,
+      collection: this.facets
+    });
+    this.layout.facetsRegion.show(this.facetsView);
+  });
+
+  App.addInitializer(function () {
+    return this.controller.fetchFilters().done(function () {
+      key.setScope('list');
+      App.router = new Router({ app: App });
+      Backbone.history.start();
+    });
+  });
+
+  var l10nXHR = window.requestMessages();
+  return jQuery.when(l10nXHR).done(function () {
+    return App.start();
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/app-new.js b/server/sonar-web/src/main/js/apps/issues/app-new.js
new file mode 100644 (file)
index 0000000..07b047b
--- /dev/null
@@ -0,0 +1,82 @@
+define([
+  './models/state',
+  './layout',
+  './models/issues',
+  'components/navigator/models/facets',
+  './models/filters',
+  './controller',
+  './router',
+  './workspace-list-view',
+  './workspace-header-view',
+  './facets-view',
+  './filters-view'
+], function (State, Layout, Issues, Facets, Filters, Controller, Router, WorkspaceListView, WorkspaceHeaderView,
+             FacetsView, FiltersView) {
+
+  var $ = jQuery,
+      App = new Marionette.Application();
+
+  App.addInitializer(function () {
+    this.state = new State();
+    this.list = new Issues();
+    this.facets = new Facets();
+    this.filters = new Filters();
+  });
+
+  App.addInitializer(function () {
+    this.layout = new Layout({ app: this });
+    $('.issues').empty().append(this.layout.render().el);
+    $('#footer').addClass('search-navigator-footer');
+  });
+
+  App.addInitializer(function () {
+    this.controller = new Controller({ app: this });
+  });
+
+  App.addInitializer(function () {
+    this.issuesView = new WorkspaceListView({
+      app: this,
+      collection: this.list
+    });
+    this.layout.workspaceListRegion.show(this.issuesView);
+    this.issuesView.bindScrollEvents();
+  });
+
+  App.addInitializer(function () {
+    this.workspaceHeaderView = new WorkspaceHeaderView({
+      app: this,
+      collection: this.list
+    });
+    this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView);
+  });
+
+  App.addInitializer(function () {
+    this.facetsView = new FacetsView({
+      app: this,
+      collection: this.facets
+    });
+    this.layout.facetsRegion.show(this.facetsView);
+  });
+
+  App.addInitializer(function () {
+    this.filtersView = new FiltersView({
+      app: this,
+      collection: this.filters
+    });
+    this.layout.filtersRegion.show(this.filtersView);
+  });
+
+  App.addInitializer(function () {
+    this.controller.fetchFilters().done(function () {
+      key.setScope('list');
+      App.router = new Router({ app: App });
+      Backbone.history.start();
+    });
+  });
+
+  var l10nXHR = window.requestMessages();
+  return jQuery.when(l10nXHR).done(function () {
+    return App.start();
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js b/server/sonar-web/src/main/js/apps/issues/component-viewer/issue-view.js
new file mode 100644 (file)
index 0000000..6d8a6f7
--- /dev/null
@@ -0,0 +1,18 @@
+define([
+  '../workspace-list-item-view'
+], function (IssueView) {
+
+  return IssueView.extend({
+    onRender: function () {
+      IssueView.prototype.onRender.apply(this, arguments);
+      this.$el.removeClass('issue-navigate-right');
+    },
+
+    serializeData: function () {
+      return _.extend(IssueView.prototype.serializeData.apply(this, arguments), {
+        showComponent: false
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js b/server/sonar-web/src/main/js/apps/issues/component-viewer/main.js
new file mode 100644 (file)
index 0000000..a702c4a
--- /dev/null
@@ -0,0 +1,204 @@
+define([
+  'components/source-viewer/main',
+  '../models/issues',
+  './issue-view',
+  '../templates'
+], function (SourceViewer, Issues, IssueView) {
+
+  var $ = jQuery;
+
+  return SourceViewer.extend({
+    events: function () {
+      return _.extend(SourceViewer.prototype.events.apply(this, arguments), {
+        'click .js-close-component-viewer': 'closeComponentViewer',
+        'click .code-issue': 'selectIssue'
+      });
+    },
+
+    initialize: function (options) {
+      SourceViewer.prototype.initialize.apply(this, arguments);
+      return this.listenTo(options.app.state, 'change:selectedIndex', this.select);
+    },
+
+    onLoaded: function () {
+      SourceViewer.prototype.onLoaded.apply(this, arguments);
+      this.bindShortcuts();
+      if (this.baseIssue != null) {
+        return this.scrollToLine(this.baseIssue.get('line'));
+      }
+    },
+
+    bindShortcuts: function () {
+      var that = this;
+      var doAction = function (action) {
+        var selectedIssueView = that.getSelectedIssueEl();
+        if (!selectedIssueView) {
+          return;
+        }
+        return selectedIssueView.find('.js-issue-' + action).click();
+      };
+      key('up', 'componentViewer', function () {
+        that.options.app.controller.selectPrev();
+        return false;
+      });
+      key('down', 'componentViewer', function () {
+        that.options.app.controller.selectNext();
+        return false;
+      });
+      key('left,backspace', 'componentViewer', function () {
+        that.options.app.controller.closeComponentViewer();
+        return false;
+      });
+      key('f', 'componentViewer', function () {
+        return doAction('transition');
+      });
+      key('a', 'componentViewer', function () {
+        return doAction('assign');
+      });
+      key('m', 'componentViewer', function () {
+        return doAction('assign-to-me');
+      });
+      key('p', 'componentViewer', function () {
+        return doAction('plan');
+      });
+      key('i', 'componentViewer', function () {
+        return doAction('set-severity');
+      });
+      return key('c', 'componentViewer', function () {
+        return doAction('comment');
+      });
+    },
+
+    unbindShortcuts: function () {
+      return key.deleteScope('componentViewer');
+    },
+
+    onClose: function () {
+      SourceViewer.prototype.onClose.apply(this, arguments);
+      this.unbindScrollEvents();
+      return this.unbindShortcuts();
+    },
+
+    select: function () {
+      var selected = this.options.app.state.get('selectedIndex'),
+          selectedIssue = this.options.app.list.at(selected);
+      if (selectedIssue.get('component') === this.model.get('key')) {
+        return this.scrollToIssue(selectedIssue.get('key'));
+      } else {
+        this.unbindShortcuts();
+        return this.options.app.controller.showComponentViewer(selectedIssue);
+      }
+    },
+
+    getSelectedIssueEl: function () {
+      var selected = this.options.app.state.get('selectedIndex');
+      if (selected == null) {
+        return null;
+      }
+      var selectedIssue = this.options.app.list.at(selected);
+      if (selectedIssue == null) {
+        return null;
+      }
+      var selectedIssueView = this.$('#issue-' + (selectedIssue.get('key')));
+      if (selectedIssueView.length > 0) {
+        return selectedIssueView;
+      } else {
+        return null;
+      }
+    },
+
+    selectIssue: function (e) {
+      var key = $(e.currentTarget).data('issue-key'),
+          issue = this.issues.find(function (issue) {
+            return issue.get('key') === key;
+          }),
+          index = this.options.app.list.indexOf(issue);
+      return this.options.app.state.set({ selectedIndex: index });
+    },
+
+    scrollToIssue: function (key) {
+      var el = this.$('#issue-' + key);
+      if (el.length > 0) {
+        var line = el.closest('[data-line-number]').data('line-number');
+        return this.scrollToLine(line);
+      } else {
+        this.unbindShortcuts();
+        var selected = this.options.app.state.get('selectedIndex'),
+            selectedIssue = this.options.app.list.at(selected);
+        return this.options.app.controller.showComponentViewer(selectedIssue);
+      }
+    },
+
+    openFileByIssue: function (issue) {
+      this.baseIssue = issue;
+      var componentKey = issue.get('component'),
+          componentUuid = issue.get('componentUuid');
+      return this.open(componentUuid, componentKey);
+    },
+
+    linesLimit: function () {
+      var line = this.LINES_LIMIT / 2;
+      if ((this.baseIssue != null) && this.baseIssue.has('line')) {
+        line = Math.max(line, this.baseIssue.get('line'));
+      }
+      return {
+        from: line - this.LINES_LIMIT / 2 + 1,
+        to: line + this.LINES_LIMIT / 2
+      };
+    },
+
+    limitIssues: function (issues) {
+      var that = this;
+      var index = this.ISSUES_LIMIT / 2;
+      if ((this.baseIssue != null) && this.baseIssue.has('index')) {
+        index = Math.max(index, this.baseIssue.get('index'));
+      }
+      return issues.filter(function (issue) {
+        return Math.abs(issue.get('index') - index) <= that.ISSUES_LIMIT / 2;
+      });
+    },
+
+    requestIssues: function () {
+      var that = this;
+      var r;
+      if (this.options.app.list.last().get('component') === this.model.get('key')) {
+        r = this.options.app.controller.fetchNextPage();
+      } else {
+        r = $.Deferred().resolve().promise();
+      }
+      return r.done(function () {
+        that.issues.reset(that.options.app.list.filter(function (issue) {
+          return issue.get('component') === that.model.key();
+        }));
+        that.issues.reset(that.limitIssues(that.issues));
+        return that.addIssuesPerLineMeta(that.issues);
+      });
+    },
+
+    renderIssues: function () {
+      this.issues.forEach(this.renderIssue, this);
+      return this.$('.source-line-issues').addClass('hidden');
+    },
+
+    renderIssue: function (issue) {
+      var issueView = new IssueView({
+        el: '#issue-' + issue.get('key'),
+        model: issue,
+        app: this.options.app
+      });
+      this.issueViews.push(issueView);
+      return issueView.render();
+    },
+
+    scrollToLine: function (line) {
+      var row = this.$('[data-line-number=' + line + ']'),
+          goal = row.length > 0 ? row.offset().top - 200 : 0;
+      return $(window).scrollTop(goal);
+    },
+
+    closeComponentViewer: function () {
+      return this.options.app.controller.closeComponentViewer();
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/controller.js b/server/sonar-web/src/main/js/apps/issues/controller.js
new file mode 100644 (file)
index 0000000..15ccdda
--- /dev/null
@@ -0,0 +1,249 @@
+define([
+  'components/navigator/controller',
+  './component-viewer/main',
+  './workspace-home-view'
+], function (Controller, ComponentViewer, HomeView) {
+
+  var $ = jQuery,
+      EXTRA_FIELDS = 'actions,transitions,assigneeName,reporterName,actionPlanName',
+      FACET_DATA_FIELDS = ['components', 'projects', 'users', 'rules', 'actionPlans', 'languages'];
+
+  return Controller.extend({
+    _facetsFromServer: function () {
+      var facets = Controller.prototype._facetsFromServer.apply(this, arguments) || [];
+      facets.push('assigned_to_me');
+      return facets;
+    },
+
+    _issuesParameters: function () {
+      return {
+        p: this.options.app.state.get('page'),
+        ps: this.pageSize,
+        s: 'FILE_LINE',
+        asc: true,
+        extra_fields: EXTRA_FIELDS,
+        facets: this._facetsFromServer().join()
+      };
+    },
+
+    _myIssuesFromResponse: function (r) {
+      var myIssuesData = _.findWhere(r.facets, { property: 'assigned_to_me' });
+      if ((myIssuesData != null) && _.isArray(myIssuesData.values) && myIssuesData.values.length > 0) {
+        return this.options.app.state.set({ myIssues: myIssuesData.values[0].count }, { silent: true });
+      } else {
+        return this.options.app.state.unset('myIssues', { silent: true });
+      }
+    },
+
+    fetchList: function (firstPage) {
+      var that = this;
+      if (firstPage == null) {
+        firstPage = true;
+      }
+      if (firstPage) {
+        this.options.app.state.set({ selectedIndex: 0, page: 1 }, { silent: true });
+        this.hideHomePage();
+        this.closeComponentViewer();
+      }
+      var data = this._issuesParameters();
+      _.extend(data, this.options.app.state.get('query'));
+      if (this.options.app.state.get('isContext')) {
+        _.extend(data, this.options.app.state.get('contextQuery'));
+      }
+      return $.get(baseUrl + '/api/issues/search', data).done(function (r) {
+        var issues = that.options.app.list.parseIssues(r);
+        if (firstPage) {
+          that.options.app.list.reset(issues);
+        } else {
+          that.options.app.list.add(issues);
+        }
+        that.options.app.list.setIndex();
+        FACET_DATA_FIELDS.forEach(function (field) {
+          that.options.app.facets[field] = r[field];
+        });
+        that.options.app.facets.reset(that._allFacets());
+        that.options.app.facets.add(_.reject(r.facets, function (f) {
+          return f.property === 'assigned_to_me';
+        }), { merge: true });
+        that._myIssuesFromResponse(r);
+        that.enableFacets(that._enabledFacets());
+        that.options.app.state.set({
+          page: r.p,
+          pageSize: r.ps,
+          total: r.total,
+          maxResultsReached: r.p * r.ps >= r.total
+        });
+        if (firstPage && that.isIssuePermalink()) {
+          return that.showComponentViewer(that.options.app.list.first());
+        }
+      });
+    },
+
+    isIssuePermalink: function () {
+      var query = this.options.app.state.get('query');
+      return (query.issues != null) && this.options.app.list.length === 1;
+    },
+
+    fetchFilters: function () {
+      var that = this;
+      return $.get(baseUrl + '/api/issue_filters/app', function (r) {
+        that.options.app.state.set({
+          canBulkChange: r.canBulkChange,
+          canManageFilters: r.canManageFilters
+        });
+        return that.options.app.filters.reset(r.favorites);
+      });
+    },
+
+    _mergeCollections: function (a, b) {
+      var collection = new Backbone.Collection(a);
+      collection.add(b, { merge: true });
+      return collection.toJSON();
+    },
+
+    requestFacet: function (id) {
+      var that = this;
+      if (id === 'assignees') {
+        return this.requestAssigneeFacet();
+      }
+      var facet = this.options.app.facets.get(id),
+          data = _.extend({ facets: id, ps: 1 }, this.options.app.state.get('query'));
+      if (this.options.app.state.get('isContext')) {
+        _.extend(data, this.options.app.state.get('contextQuery'));
+      }
+      return $.get(baseUrl + '/api/issues/search', data, function (r) {
+        FACET_DATA_FIELDS.forEach(function (field) {
+          that.options.app.facets[field] = that._mergeCollections(that.options.app.facets[field], r[field]);
+        });
+        var facetData = _.findWhere(r.facets, { property: id });
+        if (facetData != null) {
+          return facet.set(facetData);
+        }
+      });
+    },
+
+    requestAssigneeFacet: function () {
+      var that = this;
+      var facet = this.options.app.facets.get('assignees'),
+          data = _.extend({ facets: 'assignees,assigned_to_me', ps: 1 }, this.options.app.state.get('query'));
+      if (this.options.app.state.get('isContext')) {
+        _.extend(data, this.options.app.state.get('contextQuery'));
+      }
+      return $.get(baseUrl + '/api/issues/search', data, function (r) {
+        FACET_DATA_FIELDS.forEach(function (field) {
+          that.options.app.facets[field] = that._mergeCollections(that.options.app.facets[field], r[field]);
+        });
+        var facetData = _.findWhere(r.facets, { property: 'assignees' });
+        that._myIssuesFromResponse(r);
+        if (facetData != null) {
+          return facet.set(facetData);
+        }
+      });
+    },
+
+    newSearch: function () {
+      this.options.app.state.unset('filter');
+      return this.options.app.state.setQuery({ resolved: 'false' });
+    },
+
+    applyFilter: function (filter, ignoreQuery) {
+      if (ignoreQuery == null) {
+        ignoreQuery = false;
+      }
+      if (!ignoreQuery) {
+        var filterQuery = this.parseQuery(filter.get('query'));
+        this.options.app.state.setQuery(filterQuery);
+      }
+      return this.options.app.state.set({ filter: filter, changed: false });
+    },
+
+    parseQuery: function () {
+      var q = Controller.prototype.parseQuery.apply(this, arguments);
+      delete q.asc;
+      delete q.s;
+      return q;
+    },
+
+    getQuery: function (separator, addContext) {
+      if (separator == null) {
+        separator = '|';
+      }
+      if (addContext == null) {
+        addContext = false;
+      }
+      var filter = this.options.app.state.get('query');
+      if (addContext && this.options.app.state.get('isContext')) {
+        _.extend(filter, this.options.app.state.get('contextQuery'));
+      }
+      var route = [];
+      _.map(filter, function (value, property) {
+        return route.push('' + property + '=' + encodeURIComponent(value));
+      });
+      return route.join(separator);
+    },
+
+    getRoute: function () {
+      var filter = this.options.app.state.get('filter'),
+          query = Controller.prototype.getRoute.apply(this, arguments);
+      if (filter != null) {
+        if (this.options.app.state.get('changed') && query.length > 0) {
+          query = 'id=' + filter.id + '|' + query;
+        } else {
+          query = 'id=' + filter.id;
+        }
+      }
+      return query;
+    },
+
+    _prepareComponent: function (issue) {
+      return {
+        key: issue.get('component'),
+        name: issue.get('componentLongName'),
+        qualifier: issue.get('componentQualifier'),
+        project: issue.get('project'),
+        projectName: issue.get('projectLongName')
+      };
+    },
+
+    showComponentViewer: function (issue) {
+      this.options.app.layout.workspaceComponentViewerRegion.reset();
+      key.setScope('componentViewer');
+      this.options.app.issuesView.unbindScrollEvents();
+      this.options.app.state.set('component', this._prepareComponent(issue));
+      this.options.app.componentViewer = new ComponentViewer({ app: this.options.app });
+      this.options.app.layout.workspaceComponentViewerRegion.show(this.options.app.componentViewer);
+      this.options.app.layout.showComponentViewer();
+      return this.options.app.componentViewer.openFileByIssue(issue);
+    },
+
+    closeComponentViewer: function () {
+      key.setScope('list');
+      $('body').click();
+      this.options.app.state.unset('component');
+      this.options.app.layout.workspaceComponentViewerRegion.reset();
+      this.options.app.layout.hideComponentViewer();
+      this.options.app.issuesView.bindScrollEvents();
+      return this.options.app.issuesView.scrollTo();
+    },
+
+    showHomePage: function () {
+      this.fetchList();
+      this.options.app.layout.workspaceComponentViewerRegion.reset();
+      key.setScope('home');
+      this.options.app.issuesView.unbindScrollEvents();
+      this.options.app.homeView = new HomeView({ app: this.options.app });
+      this.options.app.layout.workspaceHomeRegion.show(this.options.app.homeView);
+      return this.options.app.layout.showHomePage();
+    },
+
+    hideHomePage: function () {
+      this.options.app.layout.workspaceComponentViewerRegion.reset();
+      this.options.app.layout.workspaceHomeRegion.reset();
+      key.setScope('list');
+      this.options.app.layout.hideHomePage();
+      this.options.app.issuesView.bindScrollEvents();
+      return this.options.app.issuesView.scrollTo();
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets-view.js b/server/sonar-web/src/main/js/apps/issues/facets-view.js
new file mode 100644 (file)
index 0000000..2409c25
--- /dev/null
@@ -0,0 +1,65 @@
+define([
+  'components/navigator/facets-view',
+  './facets/base-facet',
+  './facets/severity-facet',
+  './facets/status-facet',
+  './facets/project-facet',
+  './facets/module-facet',
+  './facets/assignee-facet',
+  './facets/rule-facet',
+  './facets/tag-facet',
+  './facets/resolution-facet',
+  './facets/creation-date-facet',
+  './facets/action-plan-facet',
+  './facets/file-facet',
+  './facets/reporter-facet',
+  './facets/language-facet',
+  './facets/author-facet',
+  './facets/issue-key-facet',
+  './facets/context-facet'
+], function (FacetsView, BaseFacet, SeverityFacet, StatusFacet, ProjectFacet, ModuleFacet, AssigneeFacet, RuleFacet,
+             TagFacet, ResolutionFacet, CreationDateFacet, ActionPlanFacet, FileFacet, ReporterFacet, LanguageFacet,
+             AuthorFacet, IssueKeyFacet, ContextFacet) {
+
+  return FacetsView.extend({
+    getItemView: function (model) {
+      switch (model.get('property')) {
+        case 'severities':
+          return SeverityFacet;
+        case 'statuses':
+          return StatusFacet;
+        case 'assignees':
+          return AssigneeFacet;
+        case 'resolutions':
+          return ResolutionFacet;
+        case 'createdAt':
+          return CreationDateFacet;
+        case 'projectUuids':
+          return ProjectFacet;
+        case 'moduleUuids':
+          return ModuleFacet;
+        case 'rules':
+          return RuleFacet;
+        case 'tags':
+          return TagFacet;
+        case 'actionPlans':
+          return ActionPlanFacet;
+        case 'fileUuids':
+          return FileFacet;
+        case 'reporters':
+          return ReporterFacet;
+        case 'languages':
+          return LanguageFacet;
+        case 'authors':
+          return AuthorFacet;
+        case 'issues':
+          return IssueKeyFacet;
+        case 'context':
+          return ContextFacet;
+        default:
+          return BaseFacet;
+      }
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/action-plan-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/action-plan-facet.js
new file mode 100644 (file)
index 0000000..d85fa9e
--- /dev/null
@@ -0,0 +1,68 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  var $ = jQuery;
+
+  return BaseFacet.extend({
+    template: Templates['issues-action-plan-facet'],
+
+    onRender: function () {
+      BaseFacet.prototype.onRender.apply(this, arguments);
+      var value = this.options.app.state.get('query').planned;
+      if ((value != null) && (!value || value === 'false')) {
+        return this.$('.js-facet').filter('[data-unplanned]').addClass('active');
+      }
+    },
+
+    toggleFacet: function (e) {
+      var unplanned = $(e.currentTarget).is('[data-unplanned]');
+      $(e.currentTarget).toggleClass('active');
+      if (unplanned) {
+        var checked = $(e.currentTarget).is('.active'),
+            value = checked ? 'false' : null;
+        return this.options.app.state.updateFilter({
+          planned: value,
+          actionPlans: null
+        });
+      } else {
+        return this.options.app.state.updateFilter({
+          planned: null,
+          actionPlans: this.getValue()
+        });
+      }
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          actionPlans = this.options.app.facets.actionPlans;
+      values.forEach(function (v) {
+        var key = v.val,
+            label = null;
+        if (key) {
+          var actionPlan = _.findWhere(actionPlans, { key: key });
+          if (actionPlan != null) {
+            label = actionPlan.name;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    disable: function () {
+      return this.options.app.state.updateFilter({
+        planned: null,
+        actionPlans: null
+      });
+    },
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.getValuesWithLabels()
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/assignee-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/assignee-facet.js
new file mode 100644 (file)
index 0000000..3645b72
--- /dev/null
@@ -0,0 +1,108 @@
+define([
+  './custom-values-facet',
+  '../templates'
+], function (CustomValuesFacet) {
+
+  var $ = jQuery;
+
+  return CustomValuesFacet.extend({
+    template: Templates['issues-assignee-facet'],
+
+    getUrl: function () {
+      return baseUrl + '/api/users/search';
+    },
+
+    prepareAjaxSearch: function () {
+      return {
+        quietMillis: 300,
+        url: this.getUrl(),
+        data: function (term, page) {
+          return { q: term, p: page };
+        },
+        results: window.usersToSelect2
+      };
+    },
+
+    onRender: function () {
+      CustomValuesFacet.prototype.onRender.apply(this, arguments);
+      var value = this.options.app.state.get('query').assigned;
+      if ((value != null) && (!value || value === 'false')) {
+        return this.$('.js-facet').filter('[data-unassigned]').addClass('active');
+      }
+    },
+
+    toggleFacet: function (e) {
+      var unassigned = $(e.currentTarget).is('[data-unassigned]');
+      $(e.currentTarget).toggleClass('active');
+      if (unassigned) {
+        var checked = $(e.currentTarget).is('.active'),
+            value = checked ? 'false' : null;
+        return this.options.app.state.updateFilter({
+          assigned: value,
+          assignees: null
+        });
+      } else {
+        return this.options.app.state.updateFilter({
+          assigned: null,
+          assignees: this.getValue()
+        });
+      }
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          users = this.options.app.facets.users;
+      values.forEach(function (v) {
+        var login = v.val,
+            name = '';
+        if (login) {
+          var user = _.findWhere(users, { login: login });
+          if (user != null) {
+            name = user.name;
+          }
+        }
+        v.label = name;
+      });
+      return values;
+    },
+
+    disable: function () {
+      return this.options.app.state.updateFilter({
+        assigned: null,
+        assignees: null
+      });
+    },
+
+    addCustomValue: function () {
+      var property = this.model.get('property'),
+          customValue = this.$('.js-custom-value').select2('val'),
+          value = this.getValue();
+      if (value.length > 0) {
+        value += ',';
+      }
+      value += customValue;
+      var obj = {};
+      obj[property] = value;
+      obj.assigned = null;
+      return this.options.app.state.updateFilter(obj);
+    },
+
+    sortValues: function (values) {
+      return _.sortBy(values, function (v) {
+        return v.val === '' ? -999999 : -v.count;
+      });
+    },
+
+    getNumberOfMyIssues: function () {
+      return this.options.app.state.get('myIssues');
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        myIssues: this.getNumberOfMyIssues(),
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/author-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/author-facet.js
new file mode 100644 (file)
index 0000000..ee987a3
--- /dev/null
@@ -0,0 +1,44 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+    getUrl: function () {
+      return baseUrl + '/api/issues/authors';
+    },
+
+    prepareSearch: function () {
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 2,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        formatInputTooShort: function () {
+          return tp('select2.tooShort', 2);
+        },
+        width: '100%',
+        ajax: {
+          quietMillis: 300,
+          url: this.getUrl(),
+          data: function (term) {
+            return { q: term, ps: 25 };
+          },
+          results: function (data) {
+            return {
+              more: false,
+              results: data.authors.map(function (author) {
+                return { id: author, text: author };
+              })
+            };
+          }
+        }
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/base-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/base-facet.js
new file mode 100644 (file)
index 0000000..1376e8b
--- /dev/null
@@ -0,0 +1,19 @@
+define([
+  'components/navigator/facets/base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-base-facet'],
+
+    onRender: function () {
+      BaseFacet.prototype.onRender.apply(this, arguments);
+      return this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
+    },
+
+    onClose: function () {
+      return this.$('[data-toggle="tooltip"]').tooltip('destroy');
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/context-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/context-facet.js
new file mode 100644 (file)
index 0000000..48c88c8
--- /dev/null
@@ -0,0 +1,16 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-context-facet'],
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        state: this.options.app.state.toJSON()
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/creation-date-facet.js
new file mode 100644 (file)
index 0000000..30305f3
--- /dev/null
@@ -0,0 +1,131 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  var $ = jQuery;
+
+  return BaseFacet.extend({
+    template: Templates['issues-creation-date-facet'],
+
+    events: function () {
+      return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+        'change input': 'applyFacet',
+        'click .js-select-period-start': 'selectPeriodStart',
+        'click .js-select-period-end': 'selectPeriodEnd',
+        'click .sonar-d3 rect': 'selectBar',
+        'click .js-all': 'onAllClick',
+        'click .js-last-week': 'onLastWeekClick',
+        'click .js-last-month': 'onLastMonthClick',
+        'click .js-last-year': 'onLastYearClick'
+      });
+    },
+
+    onRender: function () {
+      var that = this;
+      this.$el.toggleClass('search-navigator-facet-box-collapsed', !this.model.get('enabled'));
+      this.$('input').datepicker({
+        dateFormat: 'yy-mm-dd',
+        changeMonth: true,
+        changeYear: true
+      });
+      var props = ['createdAfter', 'createdBefore', 'createdAt'],
+          query = this.options.app.state.get('query');
+      props.forEach(function (prop) {
+        var value = query[prop];
+        if (value != null) {
+          return that.$('input[name=' + prop + ']').val(value);
+        }
+      });
+      var values = this.model.getValues();
+      if (!(_.isArray(values) && values.length > 0)) {
+        var date = moment(),
+            i, j;
+        values = [];
+        for (i = j = 0; j <= 10; i = ++j) {
+          values.push({ count: 0, val: date.toDate().toString() });
+          date = date.subtract(1, 'days');
+        }
+        values.reverse();
+      }
+      return this.$('.js-barchart').barchart(values);
+    },
+
+    selectPeriodStart: function () {
+      return this.$('.js-period-start').datepicker('show');
+    },
+
+    selectPeriodEnd: function () {
+      return this.$('.js-period-end').datepicker('show');
+    },
+
+    applyFacet: function () {
+      var obj = { createdAt: null, createdInLast: null };
+      this.$('input').each(function () {
+        var property, value;
+        property = $(this).prop('name');
+        value = $(this).val();
+        obj[property] = value;
+      });
+      return this.options.app.state.updateFilter(obj);
+    },
+
+    disable: function () {
+      return this.options.app.state.updateFilter({
+        createdAfter: null,
+        createdBefore: null,
+        createdAt: null,
+        createdInLast: null
+      });
+    },
+
+    selectBar: function (e) {
+      var periodStart = $(e.currentTarget).data('period-start'),
+          periodEnd = $(e.currentTarget).data('period-end');
+      return this.options.app.state.updateFilter({
+        createdAfter: periodStart,
+        createdBefore: periodEnd,
+        createdAt: null,
+        createdInLast: null
+      });
+    },
+
+    selectPeriod: function (period) {
+      return this.options.app.state.updateFilter({
+        createdAfter: null,
+        createdBefore: null,
+        createdAt: null,
+        createdInLast: period
+      });
+    },
+
+    onAllClick: function () {
+      return this.disable();
+    },
+
+    onLastWeekClick: function (e) {
+      e.preventDefault();
+      return this.selectPeriod('1w');
+    },
+
+    onLastMonthClick: function (e) {
+      e.preventDefault();
+      return this.selectPeriod('1m');
+    },
+
+    onLastYearClick: function (e) {
+      e.preventDefault();
+      return this.selectPeriod('1y');
+    },
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        periodStart: this.options.app.state.get('query').createdAfter,
+        periodEnd: this.options.app.state.get('query').createdBefore,
+        createdAt: this.options.app.state.get('query').createdAt,
+        createdInLast: this.options.app.state.get('query').createdInLast
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/custom-values-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/custom-values-facet.js
new file mode 100644 (file)
index 0000000..11b3aeb
--- /dev/null
@@ -0,0 +1,70 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-custom-values-facet'],
+
+    events: function () {
+      return _.extend(BaseFacet.prototype.events.apply(this, arguments), {
+        'change .js-custom-value': 'addCustomValue'
+      });
+    },
+
+    getUrl: function () {
+
+    },
+
+    onRender: function () {
+      BaseFacet.prototype.onRender.apply(this, arguments);
+      return this.prepareSearch();
+    },
+
+    prepareSearch: function () {
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 2,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        formatInputTooShort: function () {
+          return tp('select2.tooShort', 2);
+        },
+        width: '100%',
+        ajax: this.prepareAjaxSearch()
+      });
+    },
+
+    prepareAjaxSearch: function () {
+      return {
+        quietMillis: 300,
+        url: this.getUrl(),
+        data: function (term, page) {
+          return { s: term, p: page };
+        },
+        results: function (data) {
+          return { more: data.more, results: data.results };
+        }
+      };
+    },
+
+    addCustomValue: function () {
+      var property = this.model.get('property'),
+          customValue = this.$('.js-custom-value').select2('val'),
+          value = this.getValue();
+      if (value.length > 0) {
+        value += ',';
+      }
+      value += customValue;
+      var obj = {};
+      obj[property] = value;
+      return this.options.app.state.updateFilter(obj);
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/file-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/file-facet.js
new file mode 100644 (file)
index 0000000..d01340e
--- /dev/null
@@ -0,0 +1,43 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  var $ = jQuery;
+
+  return BaseFacet.extend({
+    template: Templates['issues-file-facet'],
+
+    onRender: function () {
+      BaseFacet.prototype.onRender.apply(this, arguments);
+      var maxValueWidth = _.max(this.$('.facet-stat').map(function () {
+        return $(this).outerWidth();
+      }).get());
+      return this.$('.facet-name').css('padding-right', maxValueWidth);
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          source = this.options.app.facets.components;
+      values.forEach(function (v) {
+        var key = v.val,
+            label = null;
+        if (key) {
+          var item = _.findWhere(source, { uuid: key });
+          if (item != null) {
+            label = item.longName;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/issue-key-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/issue-key-facet.js
new file mode 100644 (file)
index 0000000..2d309b8
--- /dev/null
@@ -0,0 +1,24 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-issue-key-facet'],
+
+    onRender: function () {
+      return this.$el.toggleClass('hidden', !this.options.app.state.get('query').issues);
+    },
+
+    disable: function () {
+      return this.options.app.state.updateFilter({ issues: null });
+    },
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        issues: this.options.app.state.get('query').issues
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/language-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/language-facet.js
new file mode 100644 (file)
index 0000000..3418158
--- /dev/null
@@ -0,0 +1,67 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+    getUrl: function () {
+      return baseUrl + '/api/languages/list';
+    },
+
+    prepareSearch: function () {
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 2,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        formatInputTooShort: function () {
+          return tp('select2.tooShort', 2);
+        },
+        width: '100%',
+        ajax: {
+          quietMillis: 300,
+          url: this.getUrl(),
+          data: function (term) {
+            return { q: term, ps: 0 };
+          },
+          results: function (data) {
+            return {
+              more: false,
+              results: data.languages.map(function (lang) {
+                return { id: lang.key, text: lang.name };
+              })
+            };
+          }
+        }
+      });
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          source = this.options.app.facets.languages;
+      values.forEach(function (v) {
+        var key = v.val,
+            label = null;
+        if (key) {
+          var item = _.findWhere(source, { key: key });
+          if (item != null) {
+            label = item.name;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/module-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/module-facet.js
new file mode 100644 (file)
index 0000000..96c4a1d
--- /dev/null
@@ -0,0 +1,30 @@
+define([
+  './base-facet'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          components = this.options.app.facets.components;
+      values.forEach(function (v) {
+        var uuid = v.val,
+            label = uuid;
+        if (uuid) {
+          var component = _.findWhere(components, { uuid: uuid });
+          if (component != null) {
+            label = component.longName;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(BaseFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/project-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/project-facet.js
new file mode 100644 (file)
index 0000000..ad7126b
--- /dev/null
@@ -0,0 +1,83 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+
+    getUrl: function () {
+      var q = this.options.app.state.get('contextComponentQualifier');
+      if (q === 'VW' || q === 'SVW') {
+        return baseUrl + '/api/components/search';
+      } else {
+        return baseUrl + '/api/resources/search?f=s2&q=TRK&display_uuid=true';
+      }
+    },
+
+    prepareSearch: function () {
+      var q = this.options.app.state.get('contextComponentQualifier');
+      if (q === 'VW' || q === 'SVW') {
+        return this.prepareSearchForViews();
+      } else {
+        return CustomValuesFacet.prototype.prepareSearch.apply(this, arguments);
+      }
+    },
+
+    prepareSearchForViews: function () {
+      var componentUuid = this.options.app.state.get('contextComponentUuid');
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 2,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        formatInputTooShort: function () {
+          return tp('select2.tooShort', 2);
+        },
+        width: '100%',
+        ajax: {
+          quietMillis: 300,
+          url: this.getUrl(),
+          data: function (term, page) {
+            return { q: term, componentUuid: componentUuid, p: page, ps: 25 };
+          },
+          results: function (data) {
+            return {
+              more: data.p * data.ps < data.total,
+              results: data.components.map(function (c) {
+                return { id: c.uuid, text: c.name };
+              })
+            };
+          }
+        }
+      });
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          projects = this.options.app.facets.projects;
+      values.forEach(function (v) {
+        var uuid = v.val,
+            label = '';
+        if (uuid) {
+          var project = _.findWhere(projects, { uuid: uuid });
+          if (project != null) {
+            label = project.longName;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/reporter-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/reporter-facet.js
new file mode 100644 (file)
index 0000000..6340aef
--- /dev/null
@@ -0,0 +1,46 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+    getUrl: function () {
+      return baseUrl + '/api/users/search';
+    },
+
+    prepareAjaxSearch: function () {
+      return {
+        quietMillis: 300,
+        url: this.getUrl(),
+        data: function (term, page) {
+          return { q: term, p: page };
+        },
+        results: window.usersToSelect2
+      };
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          source = this.options.app.facets.users;
+      values.forEach(function (v) {
+        var item, key, label;
+        key = v.val;
+        label = null;
+        if (key) {
+          item = _.findWhere(source, { login: key });
+          if (item != null) {
+            label = item.name;
+          }
+        }
+        v.label = label;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/resolution-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/resolution-facet.js
new file mode 100644 (file)
index 0000000..75dd3e7
--- /dev/null
@@ -0,0 +1,52 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  var $ = jQuery;
+
+  return BaseFacet.extend({
+    template: Templates['issues-resolution-facet'],
+
+    onRender: function () {
+      BaseFacet.prototype.onRender.apply(this, arguments);
+      var value = this.options.app.state.get('query').resolved;
+      if ((value != null) && (!value || value === 'false')) {
+        return this.$('.js-facet').filter('[data-unresolved]').addClass('active');
+      }
+    },
+
+    toggleFacet: function (e) {
+      var unresolved = $(e.currentTarget).is('[data-unresolved]');
+      $(e.currentTarget).toggleClass('active');
+      if (unresolved) {
+        var checked = $(e.currentTarget).is('.active'),
+            value = checked ? 'false' : null;
+        return this.options.app.state.updateFilter({
+          resolved: value,
+          resolutions: null
+        });
+      } else {
+        return this.options.app.state.updateFilter({
+          resolved: null,
+          resolutions: this.getValue()
+        });
+      }
+    },
+
+    disable: function () {
+      return this.options.app.state.updateFilter({
+        resolved: null,
+        resolutions: null
+      });
+    },
+
+    sortValues: function (values) {
+      var order = ['', 'FIXED', 'FALSE-POSITIVE', 'WONTFIX', 'REMOVED'];
+      return _.sortBy(values, function (v) {
+        return order.indexOf(v.val);
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/rule-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/rule-facet.js
new file mode 100644 (file)
index 0000000..569fcf6
--- /dev/null
@@ -0,0 +1,78 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+    prepareSearch: function () {
+      var url = baseUrl + '/api/rules/search?f=name,langName',
+          languages = this.options.app.state.get('query').languages;
+      if (languages != null) {
+        url += '&languages=' + languages;
+      }
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 2,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        formatInputTooShort: function () {
+          return tp('select2.tooShort', 2);
+        },
+        width: '100%',
+        ajax: {
+          quietMillis: 300,
+          url: url,
+          data: function (term, page) {
+            return { q: term, p: page };
+          },
+          results: function (data) {
+            var results;
+            results = data.rules.map(function (rule) {
+              return {
+                id: rule.key,
+                text: '(' + rule.langName + ') ' + rule.name
+              };
+            });
+            return {
+              more: data.p * data.ps < data.total,
+              results: results
+            };
+          }
+        }
+      });
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          rules = this.options.app.facets.rules;
+      values.forEach(function (v) {
+        var key = v.val,
+            label = '',
+            extra = '';
+        if (key) {
+          var rule = _.findWhere(rules, { key: key });
+          if (rule != null) {
+            label = rule.name;
+          }
+          if (rule != null) {
+            extra = rule.langName;
+          }
+        }
+        v.label = label;
+        v.extra = extra;
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/severity-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/severity-facet.js
new file mode 100644 (file)
index 0000000..eccaf54
--- /dev/null
@@ -0,0 +1,17 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-severity-facet'],
+
+    sortValues: function (values) {
+      var order = ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR'];
+      return _.sortBy(values, function (v) {
+        return order.indexOf(v.val);
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/status-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/status-facet.js
new file mode 100644 (file)
index 0000000..08db6d5
--- /dev/null
@@ -0,0 +1,17 @@
+define([
+  './base-facet',
+  '../templates'
+], function (BaseFacet) {
+
+  return BaseFacet.extend({
+    template: Templates['issues-status-facet'],
+
+    sortValues: function (values) {
+      var order = ['OPEN', 'RESOLVED', 'REOPENED', 'CLOSED', 'CONFIRMED'];
+      return _.sortBy(values, function (v) {
+        return order.indexOf(v.val);
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/facets/tag-facet.js b/server/sonar-web/src/main/js/apps/issues/facets/tag-facet.js
new file mode 100644 (file)
index 0000000..7752451
--- /dev/null
@@ -0,0 +1,56 @@
+define([
+  './custom-values-facet'
+], function (CustomValuesFacet) {
+
+  return CustomValuesFacet.extend({
+    prepareSearch: function () {
+      var url = baseUrl + '/api/issues/tags?ps=10',
+          tags = this.options.app.state.get('query').tags;
+      if (tags != null) {
+        url += '&tags=' + tags;
+      }
+      return this.$('.js-custom-value').select2({
+        placeholder: 'Search...',
+        minimumInputLength: 0,
+        allowClear: false,
+        formatNoMatches: function () {
+          return t('select2.noMatches');
+        },
+        formatSearching: function () {
+          return t('select2.searching');
+        },
+        width: '100%',
+        ajax: {
+          quietMillis: 300,
+          url: url,
+          data: function (term) {
+            return { q: term, ps: 10 };
+          },
+          results: function (data) {
+            var results = data.tags.map(function (tag) {
+              return { id: tag, text: tag };
+            });
+            return { more: false, results: results };
+          }
+        }
+      });
+    },
+
+    getValuesWithLabels: function () {
+      var values = this.model.getValues(),
+          tags = this.options.app.facets.tags;
+      values.forEach(function (v) {
+        v.label = v.val;
+        v.extra = '';
+      });
+      return values;
+    },
+
+    serializeData: function () {
+      return _.extend(CustomValuesFacet.prototype.serializeData.apply(this, arguments), {
+        values: this.sortValues(this.getValuesWithLabels())
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/filters-view.js b/server/sonar-web/src/main/js/apps/issues/filters-view.js
new file mode 100644 (file)
index 0000000..03813ef
--- /dev/null
@@ -0,0 +1,93 @@
+define([
+  './templates'
+], function () {
+
+  var $ = jQuery;
+
+  return Marionette.ItemView.extend({
+    template: Templates['issues-filters'],
+
+    events: {
+      'click .js-toggle-filters': 'toggleFilters',
+      'click .js-filter': 'applyFilter',
+      'click .js-filter-save-as': 'saveAs',
+      'click .js-filter-save': 'save',
+      'click .js-filter-copy': 'copy',
+      'click .js-filter-edit': 'edit'
+    },
+
+    initialize: function (options) {
+      var that = this;
+      this.listenTo(options.app.state, 'change:filter', this.render);
+      this.listenTo(options.app.state, 'change:changed', this.render);
+      this.listenTo(options.app.filters, 'all', this.render);
+      window.onSaveAs = window.onCopy = window.onEdit = function (id) {
+        $('#modal').dialog('close');
+        return that.options.app.controller.fetchFilters().done(function () {
+          var filter = that.collection.get(id);
+          return filter.fetch().done(function () {
+            return that.options.app.controller.applyFilter(filter);
+          });
+        });
+      };
+    },
+
+    onRender: function () {
+      this.$el.toggleClass('search-navigator-filters-selected', this.options.app.state.has('filter'));
+    },
+
+    toggleFilters: function (e) {
+      var that = this;
+      e.stopPropagation();
+      this.$('.search-navigator-filters-list').toggle();
+      return $('body').on('click.issues-filters', function () {
+        $('body').off('click.issues-filters');
+        return that.$('.search-navigator-filters-list').hide();
+      });
+    },
+
+    applyFilter: function (e) {
+      var that = this;
+      var id = $(e.currentTarget).data('id'),
+          filter = this.collection.get(id);
+      return filter.fetch().done(function () {
+        return that.options.app.controller.applyFilter(filter);
+      });
+    },
+
+    saveAs: function () {
+      var query = this.options.app.controller.getQuery('&'),
+          url = baseUrl + '/issues/save_as_form?' + query;
+      window.openModalWindow(url, {});
+    },
+
+    save: function () {
+      var that = this;
+      var query = this.options.app.controller.getQuery('&'),
+          url = baseUrl + '/issues/save/' + (this.options.app.state.get('filter').id) + '?' + query;
+      return $.post(url).done(function () {
+        return that.options.app.state.set({ changed: false });
+      });
+    },
+
+    copy: function () {
+      var url = baseUrl + '/issues/copy_form/' + (this.options.app.state.get('filter').id);
+      window.openModalWindow(url, {});
+    },
+
+    edit: function () {
+      var url = baseUrl + '/issues/edit_form/' + (this.options.app.state.get('filter').id);
+      window.openModalWindow(url, {});
+    },
+
+    serializeData: function () {
+      var filter = this.options.app.state.get('filter');
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        state: this.options.app.state.toJSON(),
+        filter: filter != null ? filter.toJSON() : null,
+        currentUser: window.SS.user
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/issue-filter-view.js b/server/sonar-web/src/main/js/apps/issues/issue-filter-view.js
new file mode 100644 (file)
index 0000000..67f9a61
--- /dev/null
@@ -0,0 +1,29 @@
+define([
+  'components/issue/views/action-options-view',
+  './templates'
+], function (ActionOptionsView) {
+
+  var $ = jQuery;
+
+  return ActionOptionsView.extend({
+    template: Templates['issues-issue-filter-form'],
+
+    selectInitialOption: function () {
+      return this.makeActive(this.getOptions().first());
+    },
+
+    selectOption: function (e) {
+      var property = $(e.currentTarget).data('property'),
+          value = $(e.currentTarget).data('value');
+      this.trigger('select', property, value);
+      return ActionOptionsView.prototype.selectOption.apply(this, arguments);
+    },
+
+    serializeData: function () {
+      return _.extend(ActionOptionsView.prototype.serializeData.apply(this, arguments), {
+        s: this.model.get('severity')
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/layout.js b/server/sonar-web/src/main/js/apps/issues/layout.js
new file mode 100644 (file)
index 0000000..06b2f89
--- /dev/null
@@ -0,0 +1,55 @@
+define([
+  './templates'
+], function () {
+
+  var $ = jQuery;
+  return Marionette.Layout.extend({
+    template: Templates['issues-layout'],
+
+    regions: {
+      filtersRegion: '.search-navigator-filters',
+      facetsRegion: '.search-navigator-facets',
+      workspaceHeaderRegion: '.search-navigator-workspace-header',
+      workspaceListRegion: '.search-navigator-workspace-list',
+      workspaceComponentViewerRegion: '.issues-workspace-component-viewer',
+      workspaceHomeRegion: '.issues-workspace-home'
+    },
+
+    onRender: function () {
+      if (this.options.app.state.get('isContext')) {
+        this.$(this.filtersRegion.el).addClass('hidden');
+      }
+      $('.search-navigator').addClass('sticky');
+      var top = $('.search-navigator').offset().top;
+      this.$('.search-navigator-workspace-header').css({ top: top });
+      this.$('.search-navigator-side').css({ top: top }).isolatedScroll();
+    },
+
+    showSpinner: function (region) {
+      return this[region].show(new Marionette.ItemView({
+        template: _.template('<i class="spinner"></i>')
+      }));
+    },
+
+    showComponentViewer: function () {
+      this.scroll = $(window).scrollTop();
+      $('.issues').addClass('issues-extended-view');
+    },
+
+    hideComponentViewer: function () {
+      $('.issues').removeClass('issues-extended-view');
+      if (this.scroll != null) {
+        $(window).scrollTop(this.scroll);
+      }
+    },
+
+    showHomePage: function () {
+      $('.issues').addClass('issues-home-view');
+    },
+
+    hideHomePage: function () {
+      $('.issues').removeClass('issues-home-view');
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/facet.js b/server/sonar-web/src/main/js/apps/issues/models/facet.js
new file mode 100644 (file)
index 0000000..3716ba5
--- /dev/null
@@ -0,0 +1,20 @@
+define(function () {
+
+  return Backbone.Model.extend({
+    idAttribute: 'property',
+
+    defaults: {
+      enabled: false
+    },
+
+    getValues: function () {
+      return this.get('values') || [];
+    },
+
+    toggle: function () {
+      var enabled = this.get('enabled');
+      return this.set({ enabled: !enabled });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/facets.js b/server/sonar-web/src/main/js/apps/issues/models/facets.js
new file mode 100644 (file)
index 0000000..31c685d
--- /dev/null
@@ -0,0 +1,9 @@
+define([
+  './facet'
+], function (Facet) {
+
+  return Backbone.Collection.extend({
+    model: Facet
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/filter.js b/server/sonar-web/src/main/js/apps/issues/models/filter.js
new file mode 100644 (file)
index 0000000..42b1150
--- /dev/null
@@ -0,0 +1,17 @@
+define(function () {
+
+  return Backbone.Model.extend({
+    url: function () {
+      return '/api/issue_filters/show/' + this.id;
+    },
+
+    parse: function (r) {
+      if (r.filter != null) {
+        return r.filter;
+      } else {
+        return r;
+      }
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/filters.js b/server/sonar-web/src/main/js/apps/issues/models/filters.js
new file mode 100644 (file)
index 0000000..bb66327
--- /dev/null
@@ -0,0 +1,9 @@
+define([
+  './filter'
+], function (Filter) {
+
+  return Backbone.Collection.extend({
+    model: Filter
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/issues.js b/server/sonar-web/src/main/js/apps/issues/models/issues.js
new file mode 100644 (file)
index 0000000..af41dd3
--- /dev/null
@@ -0,0 +1,65 @@
+define([
+  'components/issue/models/issue'
+], function (Issue) {
+
+  return Backbone.Collection.extend({
+    model: Issue,
+
+    url: function () {
+      return baseUrl + '/api/issues/search';
+    },
+
+    parseIssues: function (r) {
+      var find = function (source, key, keyField) {
+        var searchDict = {};
+        searchDict[keyField || 'key'] = key;
+        return _.findWhere(source, searchDict) || key;
+      };
+      return r.issues.map(function (issue, index) {
+        var component = find(r.components, issue.component),
+            project = find(r.projects, issue.project),
+            subProject = find(r.components, issue.subProject),
+            rule = find(r.rules, issue.rule),
+            assignee = find(r.users, issue.assignee, 'login');
+        _.extend(issue, { index: index });
+        if (component) {
+          _.extend(issue, {
+            componentUuid: component.uuid,
+            componentLongName: component.longName,
+            componentQualifier: component.qualifier
+          });
+        }
+        if (project) {
+          _.extend(issue, {
+            projectLongName: project.longName,
+            projectUuid: project.uuid
+          });
+        }
+        if (subProject) {
+          _.extend(issue, {
+            subProjectLongName: subProject.longName,
+            subProjectUuid: subProject.uuid
+          });
+        }
+        if (rule) {
+          _.extend(issue, {
+            ruleName: rule.name
+          });
+        }
+        if (assignee) {
+          _.extend(issue, {
+            assigneeEmail: assignee.email
+          });
+        }
+        return issue;
+      });
+    },
+
+    setIndex: function () {
+      return this.forEach(function (issue, index) {
+        return issue.set({ index: index });
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/models/state.js b/server/sonar-web/src/main/js/apps/issues/models/state.js
new file mode 100644 (file)
index 0000000..38814d7
--- /dev/null
@@ -0,0 +1,58 @@
+define([
+  'components/navigator/models/state'
+], function (State) {
+
+  return State.extend({
+    defaults: {
+      page: 1,
+      maxResultsReached: false,
+      query: {},
+      facets: ['severities', 'resolutions'],
+      isContext: false,
+      allFacets: [
+        'issues',
+        'severities',
+        'resolutions',
+        'statuses',
+        'createdAt',
+        'rules',
+        'tags',
+        'projectUuids',
+        'moduleUuids',
+        'directories',
+        'fileUuids',
+        'assignees',
+        'reporters',
+        'authors',
+        'languages',
+        'actionPlans'
+      ],
+      facetsFromServer: [
+        'severities',
+        'statuses',
+        'resolutions',
+        'actionPlans',
+        'projectUuids',
+        'directories',
+        'rules',
+        'moduleUuids',
+        'tags',
+        'assignees',
+        'reporters',
+        'authors',
+        'fileUuids',
+        'languages',
+        'createdAt'
+      ],
+      transform: {
+        'resolved': 'resolutions',
+        'assigned': 'assignees',
+        'planned': 'actionPlans',
+        'createdBefore': 'createdAt',
+        'createdAfter': 'createdAt',
+        'createdInLast': 'createdAt'
+      }
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/router.js b/server/sonar-web/src/main/js/apps/issues/router.js
new file mode 100644 (file)
index 0000000..519689e
--- /dev/null
@@ -0,0 +1,45 @@
+define([
+  'components/navigator/router'
+], function (Router) {
+
+  return Router.extend({
+    routes: {
+      '': 'home',
+      ':query': 'index'
+    },
+
+    initialize: function (options) {
+      Router.prototype.initialize.apply(this, arguments);
+      this.listenTo(options.app.state, 'change:filter', this.updateRoute);
+    },
+
+    home: function () {
+      if (this.options.app.state.get('isContext')) {
+        return this.navigate('resolved=false', { trigger: true, replace: true });
+      } else {
+        return this.options.app.controller.showHomePage();
+      }
+    },
+
+    index: function (query) {
+      var that = this;
+      query = this.options.app.controller.parseQuery(query);
+      if (query.id != null) {
+        var filter = this.options.app.filters.get(query.id);
+        delete query.id;
+        return filter.fetch().done(function () {
+          if (Object.keys(query).length > 0) {
+            that.options.app.controller.applyFilter(filter, true);
+            that.options.app.state.setQuery(query);
+            that.options.app.state.set({ changed: true });
+          } else {
+            that.options.app.controller.applyFilter(filter);
+          }
+        });
+      } else {
+        return this.options.app.state.setQuery(query);
+      }
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/_issues-filter-name.hbs b/server/sonar-web/src/main/js/apps/issues/templates/_issues-filter-name.hbs
new file mode 100644 (file)
index 0000000..22b760e
--- /dev/null
@@ -0,0 +1,18 @@
+{{#if filter.name}}
+  {{filter.name}}
+  <span class='note nowrap'>
+    {{#unless filter.shared}}
+      [{{t 'issue_filter.private'}}]
+    {{else}}
+      {{#eq filter.user currentUser}}
+        [{{t 'issue_filter.shared_with_all_users'}}]
+      {{else}}
+        {{#if filter.user}}
+          [{{t 'issue_filter.shared'}}]
+        {{/if}}
+      {{/eq}}
+    {{/unless}}
+  </span>
+{{else}}
+  {{t 'issues'}}
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/_issues-facet-header.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/_issues-facet-header.hbs
new file mode 100644 (file)
index 0000000..8e012d1
--- /dev/null
@@ -0,0 +1,4 @@
+<a class='search-navigator-facet-header js-facet-toggle'>
+  <i class='icon-checkbox {{#if enabled}}icon-checkbox-checked{{/if}}'></i>
+  {{t 'issues.facet' property}}
+</a>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-action-plan-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-action-plan-facet.hbs
new file mode 100644 (file)
index 0000000..e934608
--- /dev/null
@@ -0,0 +1,18 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    {{#eq val ''}}
+      {{! unplanned }}
+      <a class='facet search-navigator-facet js-facet' data-unplanned title='{{t 'issue.unplanned'}}'>
+        <span class='facet-name'>{{t 'issue.unplanned'}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{else}}
+      <a class='facet search-navigator-facet js-facet' data-value='{{val}}' title='{{label}}'>
+        <span class='facet-name'>{{label}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{/eq}}
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-assignee-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-assignee-facet.hbs
new file mode 100644 (file)
index 0000000..44d4b34
--- /dev/null
@@ -0,0 +1,30 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#notNull myIssues}}
+    <a class='facet search-navigator-facet js-facet' data-value='__me__' title='{{t 'me'}}'>
+      <span class='facet-name'>{{t 'me'}}</span>
+      <span class='facet-stat'>{{myIssues}}</span>
+    </a>
+    <hr>
+  {{/notNull}}
+
+  {{#each values}}
+    {{#eq val ''}}
+    {{! unassigned }}
+      <a class='facet search-navigator-facet js-facet' data-unassigned title='{{t 'unassigned'}}'>
+        <span class='facet-name'>{{t 'unassigned'}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{else}}
+      <a class='facet search-navigator-facet js-facet' data-value='{{val}}' title='{{label}}'>
+        <span class='facet-name'>{{label}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{/eq}}
+  {{/each}}
+
+  <div class='search-navigator-facet-custom-value'>
+    <input type='hidden' class='js-custom-value'>
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-base-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-base-facet.hbs
new file mode 100644 (file)
index 0000000..d8b5f0b
--- /dev/null
@@ -0,0 +1,10 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    <a class='facet search-navigator-facet js-facet' data-value='{{val}}' title='{{default label val}}'>
+      <span class='facet-name'>{{default label val}}</span>
+      <span class='facet-stat'>{{numberShort count}}</span>
+    </a>
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-context-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-context-facet.hbs
new file mode 100644 (file)
index 0000000..5c570f8
--- /dev/null
@@ -0,0 +1,3 @@
+<div class='search-navigator-facet-query'>
+  Issues of &nbsp;&nbsp; {{qualifierIcon state.contextComponentQualifier}}&nbsp;{{state.contextComponentName}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-creation-date-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-creation-date-facet.hbs
new file mode 100644 (file)
index 0000000..9003a17
--- /dev/null
@@ -0,0 +1,32 @@
+{{> '_issues-facet-header'}}
+
+{{#if createdAt}}
+  <input type='hidden' name='createdAt'>
+  <div class='search-navigator-facet-container'>
+    {{dt createdAt}} ({{fromNow createdAt}})
+  </div>
+{{else}}
+  <div class='search-navigator-facet-container'>
+    <div class='js-barchart' data-height='75' {{#if periodEnd}}data-end-date='{{periodEnd}}'{{/if}}></div>
+    <div class='search-navigator-date-facet-selection'>
+      <a class='js-select-period-start search-navigator-date-facet-selection-dropdown-left'>
+        {{#if periodStart}}{{d periodStart}}{{else}}Past{{/if}}&nbsp;<i class='icon-dropdown'></i>
+      </a>
+      <a class='js-select-period-end search-navigator-date-facet-selection-dropdown-right'>
+        {{#if periodEnd}}{{d periodEnd}}{{else}}Now{{/if}}&nbsp;<i class='icon-dropdown'></i>
+      </a>
+      <input class='js-period-start search-navigator-date-facet-selection-input-left'
+             type='text' value='{{#if periodStart}}{{ds periodStart}}{{/if}}' name='createdAfter'>
+      <input class='js-period-end search-navigator-date-facet-selection-input-right'
+             type='text' value='{{#if periodEnd}}{{ds periodEnd}}{{/if}}' name='createdBefore'>
+    </div>
+
+    <div class='spacer-top'>
+      <span class='spacer-right'>{{t 'issues.facet.createdAt.or'}}</span>
+      <a class='js-all spacer-right' href='#'>{{t 'issues.facet.createdAt.all'}}</a>
+      <a class='js-last-week spacer-right {{#eq createdInLast '1w'}}active-link{{/eq}}' href='#'>{{t 'issues.facet.createdAt.last_week'}}</a>
+      <a class='js-last-month spacer-right {{#eq createdInLast '1m'}}active-link{{/eq}}' href='#'>{{t 'issues.facet.createdAt.last_month'}}</a>
+      <a class='js-last-year {{#eq createdInLast '1y'}}active-link{{/eq}}' href='#'>{{t 'issues.facet.createdAt.last_year'}}</a>
+    </div>
+  </div>
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-custom-values-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-custom-values-facet.hbs
new file mode 100644 (file)
index 0000000..30372e8
--- /dev/null
@@ -0,0 +1,14 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    <a class='facet search-navigator-facet js-facet' data-value='{{val}}' title='{{#if extra}}({{extra}}) {{/if}}{{default label val}}'>
+      <span class='facet-name'>{{default label val}}</span>
+      <span class='facet-stat'>{{numberShort count}}</span>
+    </a>
+  {{/each}}
+
+  <div class='search-navigator-facet-custom-value'>
+    <input type='hidden' class='js-custom-value'>
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-file-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-file-facet.hbs
new file mode 100644 (file)
index 0000000..c070fb7
--- /dev/null
@@ -0,0 +1,10 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list search-navigator-facet-list-align-right'>
+  {{#each values}}
+    <a class='facet search-navigator-facet js-facet' data-value='{{val}}' title='{{default label val}}'>
+      <span class='facet-name'>{{default label val}}</span>
+      <span class='facet-stat'>{{numberShort count}}</span>
+    </a>
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-issue-key-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-issue-key-facet.hbs
new file mode 100644 (file)
index 0000000..540e9a0
--- /dev/null
@@ -0,0 +1,7 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-container'>
+  <div class='facet search-navigator-facet active' style='cursor: default;'>
+    <span class='facet-name'>{{issues}}</span>
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-resolution-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-resolution-facet.hbs
new file mode 100644 (file)
index 0000000..6fb81c5
--- /dev/null
@@ -0,0 +1,20 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    {{#eq val ''}}
+    {{! unresolved }}
+      <a class='facet search-navigator-facet search-navigator-facet-half js-facet' data-unresolved
+         title='{{t 'issue.unresolved.description'}}' data-toggle='tooltip' data-placement='right'>
+        <span class='facet-name'>{{t 'unresolved'}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{else}}
+      <a class='facet search-navigator-facet search-navigator-facet-half js-facet' data-value='{{val}}'
+         title='{{t 'issue.resolution' val 'description'}}' data-toggle='tooltip' data-placement='right'>
+        <span class='facet-name'>{{t 'issue.resolution' val}}</span>
+        <span class='facet-stat'>{{numberShort count}}</span>
+      </a>
+    {{/eq}}
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-severity-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-severity-facet.hbs
new file mode 100644 (file)
index 0000000..92ff7b5
--- /dev/null
@@ -0,0 +1,11 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    <a class='facet search-navigator-facet search-navigator-facet-half js-facet'
+       data-value='{{val}}' title='{{t 'severity' val 'description'}}' data-toggle='tooltip' data-placement='right'>
+      <span class='facet-name'>{{severityIcon val}} {{t 'severity' val}}</span>
+      <span class='facet-stat'>{{numberShort count}}</span>
+    </a>
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-status-facet.hbs b/server/sonar-web/src/main/js/apps/issues/templates/facets/issues-status-facet.hbs
new file mode 100644 (file)
index 0000000..55a78a6
--- /dev/null
@@ -0,0 +1,11 @@
+{{> '_issues-facet-header'}}
+
+<div class='search-navigator-facet-list'>
+  {{#each values}}
+    <a class='facet search-navigator-facet search-navigator-facet-half js-facet'
+       data-value='{{val}}' title='{{t 'issue.status' val 'description'}}' data-toggle='tooltip' data-placement='right'>
+      <span class='facet-name'>{{statusIcon val}} {{t 'issue.status' val}}</span>
+      <span class='facet-stat'>{{numberShort count}}</span>
+    </a>
+  {{/each}}
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs
new file mode 100644 (file)
index 0000000..e010deb
--- /dev/null
@@ -0,0 +1,55 @@
+<h1 class='page-title dropdown'>
+  {{#if state.canManageFilters}}
+    <a class='search-navigator-filters-show-list dropdown-toggle' data-toggle='dropdown'>
+      <i class='icon-list'></i><span class='issues-filters-name'>{{> '_issues-filter-name'}}</span>
+    </a>
+    <ul class='dropdown-menu'>
+      {{#each items}}
+        <li>
+          <a class='search-navigator-filters-button search-navigator-filters-filter js-filter' data-id='{{id}}'>
+            {{name}}
+          </a>
+        </li>
+      {{/each}}
+      {{#notEmpty items}}
+        <li class='divider'></li>
+      {{/notEmpty}}
+      <li>
+        <a class='search-navigator-filters-manage' href='{{link '/issues/manage'}}'>{{t 'manage'}}</a>
+      </li>
+    </ul>
+    {{#if filter.description}}
+      <div class='search-navigator-filters-description'>{{filter.description}}</div>
+    {{/if}}
+  {{else}}
+    <span class='search-navigator-filters-name'>{{t 'issues'}}</span>
+  {{/if}}
+</h1>
+
+<div class='page-actions'>
+  <div class='button-group'>
+    {{#if state.canManageFilters}}
+      {{#if filter.canModify}}
+        {{#if state.changed}}
+          <button class='js-filter-save' id='issues-filter-save'>{{t 'save'}}</button>
+        {{/if}}
+      {{/if}}
+
+      {{#unless filter.id}}
+        <button class='js-filter-save-as' id='issues-filter-save-as'>{{t 'save_as'}}</button>
+      {{/unless}}
+
+      {{#if filter.id}}
+        {{#unless state.changed}}
+          <button class='js-filter-copy' id='issues-filter-copy'>{{t 'copy'}}</button>
+        {{/unless}}
+      {{/if}}
+
+      {{#if filter.canModify}}
+        {{#if filter.id}}
+          <button class='js-filter-edit' id='issues-filter-edit'>{{t 'edit'}}</button>
+        {{/if}}
+      {{/if}}
+    {{/if}}
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter-form.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter-form.hbs
new file mode 100644 (file)
index 0000000..1954f67
--- /dev/null
@@ -0,0 +1,71 @@
+<h6>{{t 'issue.filter_similar_issues'}}</h6>
+
+<div class='issue-action-options'>
+  <a href='#' class='issue-action-option' data-property='severities' data-value='{{s}}'>
+    {{severityIcon severity}}&nbsp;{{t 'severity' severity}}
+  </a>
+
+  <a href='#' class='issue-action-option' data-property='statuses' data-value='{{status}}'>
+    {{statusIcon status}}&nbsp;{{t 'issue.status' status}}
+  </a>
+
+  {{#if resolution}}
+    <a href='#' class='issue-action-option' data-property='resolutions' data-value='{{resolution}}'>
+      {{t 'issue.resolution' resolution}}
+    </a>
+  {{else}}
+    <a href='#' class='issue-action-option' data-property='resolved' data-value='false'>
+      {{t 'unresolved'}}
+    </a>
+  {{/if}}
+
+  {{#if assignee}}
+    <a href='#' class='issue-action-option' data-property='assignees' data-value='{{assignee}}'>
+      {{t 'assigned_to'}} {{assigneeName}}
+    </a>
+  {{else}}
+    <a href='#' class='issue-action-option' data-property='assigned' data-value='false'>
+      {{t 'unassigned'}}
+    </a>
+  {{/if}}
+
+  {{#if actionPlan}}
+    <a href='#' class='issue-action-option' data-property='actionPlans' data-value='{{actionPlan}}'>
+      {{t 'issue.planned_for'}} {{actionPlanName}}
+    </a>
+  {{else}}
+    <a href='#' class='issue-action-option' data-property='planned' data-value='false'>
+      {{t 'issue.unplanned'}}
+    </a>
+  {{/if}}
+
+  <hr>
+
+  <a href='#' class='issue-action-option' data-property='rules' data-value='{{rule}}'>
+    {{limitString ruleName}}
+  </a>
+
+  {{#each tags}}
+    <a href='#' class='issue-action-option' data-property='tags' data-value='{{this}}'>
+      <i class='icon-tags icon-half-transparent'></i>&nbsp;{{this}}
+    </a>
+  {{/each}}
+
+  <hr>
+
+  <a href='#' class='issue-action-option' data-property='projectUuids' data-value='{{projectUuid}}'>
+    {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
+  </a>
+
+  {{#if subProject}}
+    <a href='#' class='issue-action-option' data-property='moduleUuids' data-value='{{subProjectUuid}}'>
+      {{qualifierIcon 'BRC'}}&nbsp;{{subProjectLongName}}
+    </a>
+  {{/if}}
+
+  <a href='#' class='issue-action-option' data-property='fileUuids' data-value='{{componentUuid}}'>
+    {{qualifierIcon componentQualifier}}&nbsp;{{fileFromPath componentLongName}}
+  </a>
+</div>
+
+<div class='bubble-popup-arrow'></div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-issue-filter.hbs
new file mode 100644 (file)
index 0000000..3ab307c
--- /dev/null
@@ -0,0 +1,5 @@
+<div class='issue-meta'>
+  <a class='issue-action issue-action-with-options js-issue-filter' href='#'>
+    <i class='icon-filter icon-half-transparent'></i>&nbsp;<i class='icon-dropdown'></i>
+  </a>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-layout.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-layout.hbs
new file mode 100644 (file)
index 0000000..cd434ab
--- /dev/null
@@ -0,0 +1,11 @@
+<div class='search-navigator-side'>
+  <div class='search-navigator-filters'></div>
+  <div class='search-navigator-facets'></div>
+</div>
+
+<div class='search-navigator-workspace'>
+  <div class='search-navigator-workspace-header'></div>
+  <div class='search-navigator-workspace-list'></div>
+  <div class='issues-workspace-component-viewer'></div>
+  <div class='issues-workspace-home'></div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-header.hbs
new file mode 100644 (file)
index 0000000..2c7d2d6
--- /dev/null
@@ -0,0 +1,37 @@
+<div class='issues-header-component nowrap'>
+  {{#if state.component}}
+    <a class='js-back'>{{t 'issues.return_to_list'}}</a>&nbsp;&nbsp;&nbsp;
+
+    {{#with state.component}}
+      {{qualifierIcon 'TRK'}}&nbsp;<a href='{{dashboardUrl project}}' title='{{projectName}}'>{{projectName}}</a>
+      &nbsp;&nbsp;
+      {{qualifierIcon qualifier}}&nbsp;<a href='{{dashboardUrl key}}' title='{{name}}'>{{name}}</a>
+    {{/with}}
+  {{else}}
+    &nbsp;
+  {{/if}}
+</div>
+
+
+<div class='search-navigator-header-actions'>
+  {{#notNull state.total}}
+    <div class='search-navigator-header-pagination'>
+      {{#gt state.total 0}}
+        <a class='js-prev icon-prev' title='{{t 'paging_previous'}}'></a>
+        <span class='current'>{{sum state.selectedIndex 1}} / <span id='issues-total'>{{state.total}}</span></span>
+        <a class='js-next icon-next' title='{{t 'paging_next'}}'></a>
+      {{else}}
+        <span class='current'>0 / <span id='issues-total'>0</span></span>
+      {{/gt}}
+    </div>
+  {{/notNull}}
+
+
+  <div class='search-navigator-header-buttons button-group'>
+    <button id='issues-reload' class='js-reload'>{{t 'reload'}}</button>
+    <button class='js-new-search' id='issues-new-search'>{{t 'issue_filter.new_search'}}</button>
+    {{#if state.canBulkChange}}
+      <button id='issues-bulk-change' class='js-bulk-change'>{{t 'bulk_change'}}</button>
+    {{/if}}
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs
new file mode 100644 (file)
index 0000000..374dad3
--- /dev/null
@@ -0,0 +1,77 @@
+<div class='spacer-top spacer-bottom'>
+  <div class='columns'>
+    <div class='column-half {{#unless user}}column-one{{/unless}}'>
+      <h3 class='text-center'>{{t 'issues.home.recent_issues'}}</h3>
+      <p class='note text-center'>({{t 'issues.home.over_last_week'}})</p>
+
+      <div class='spacer-top text-center js-barchart' data-height='75' data-width='300'></div>
+      <h4 class='spacer-top spacer-bottom text-center'>{{t 'issues.home.projects'}}</h4>
+      <table class='data zebra spacer-top'>
+        {{#each projects}}
+          <tr>
+            <td>{{qualifierIcon 'TRK'}}&nbsp;<a href='{{issuesHomeLink 'projectUuids' val}}'>{{label}}</a></td>
+            <td class='thin text-right'>+{{numberShort count}}</td>
+          </tr>
+        {{/each}}
+      </table>
+      <h4 class='spacer-top spacer-bottom text-center'>{{t 'issues.home.authors'}}</h4>
+      <table class='data zebra spacer-top'>
+        {{#each authors}}
+          <tr>
+            <td><a href='{{issuesHomeLink 'authors' val}}'>{{val}}</a></td>
+            <td class='thin text-right'>+{{numberShort count}}</td>
+          </tr>
+        {{/each}}
+      </table>
+      <h4 class='spacer-top spacer-bottom text-center'>{{t 'issues.home.tags'}}</h4>
+      <ul class='list-inline'>
+        {{#each tags}}
+          <li><a class='link-no-underline' style='font-size: {{size}}px;' data-toggle='tooltip' data-placement='bottom'
+                 href='{{issuesHomeLink 'tags' val}}'
+                 title='+{{numberShort count}}'>{{val}}</a></li>
+        {{/each}}
+      </ul>
+    </div>
+
+    {{#if user}}
+      <div class='column-half'>
+        <h3 class='text-center'>{{t 'issues.home.my_recent_issues'}}</h3>
+        <p class='note text-center'>({{t 'issues.home.over_last_week'}})</p>
+
+        <div class='spacer-top text-center js-my-barchart' data-height='75' data-width='300'></div>
+        {{#notEmpty myProjects}}
+          <h4 class='spacer-top spacer-bottom text-center'>{{t 'issues.home.projects'}}</h4>
+          <table class='data zebra spacer-top'>
+            {{#each myProjects}}
+              <tr>
+                <td>{{qualifierIcon 'TRK'}}&nbsp;<a href='{{myIssuesHomeLink 'projectUuids' val}}'>{{label}}</a></td>
+                <td class='thin text-right'>+{{numberShort count}}</td>
+              </tr>
+            {{/each}}
+          </table>
+        {{/notEmpty}}
+        {{#notEmpty myTags}}
+          <h4 class='spacer-top spacer-bottom text-center'>{{t 'issues.home.tags'}}</h4>
+          <ul class='list-inline'>
+            {{#each myTags}}
+              <li><a class='link-no-underline' style='font-size: {{size}}px;' data-toggle='tooltip' data-placement='bottom'
+                     href='{{myIssuesHomeLink 'tags' val}}'
+                     title='+{{numberShort count}}'>{{val}}</a></li>
+            {{/each}}
+          </ul>
+        {{/notEmpty}}
+        {{#notEmpty filters}}
+          <h3 class='spacer-bottom text-center' style='padding-top: 12px; margin-top: 20px; border-top: 1px solid #efefef;'>
+            {{t 'issues.home.my_filters'}}</h3>
+          <ul class='list-inline'>
+            {{#each filters}}
+              <li>
+                <a href='{{issueFilterHomeLink id}}'>{{name}}</a>
+              </li>
+            {{/each}}
+          </ul>
+        {{/notEmpty}}
+      </div>
+    {{/if}}
+  </div>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list-component.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list-component.hbs
new file mode 100644 (file)
index 0000000..73d15ac
--- /dev/null
@@ -0,0 +1,13 @@
+<div class='issues-workspace-list-component'>
+  <a class='issues-workspace-list-component-part' href='{{dashboardUrl project}}'>
+    {{qualifierIcon 'TRK'}}&nbsp;{{projectLongName}}
+  </a>
+  {{#if subProject}}
+    <a class='issues-workspace-list-component-part' href='{{dashboardUrl subProject}}'>
+      {{qualifierIcon 'TRK'}}&nbsp;{{subProjectLongName}}
+    </a>
+  {{/if}}
+  <a class='issues-workspace-list-component-part' href='{{dashboardUrl component}}'>
+    {{qualifierIcon componentQualifier}}&nbsp;{{componentLongName}}
+  </a>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-list.hbs
new file mode 100644 (file)
index 0000000..a81b673
--- /dev/null
@@ -0,0 +1,5 @@
+<div class='js-list'></div>
+
+<div class='search-navigator-workspace-list-more js-more'>
+  <i class='spinner'></i>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-header-view.js
new file mode 100644 (file)
index 0000000..620b68b
--- /dev/null
@@ -0,0 +1,47 @@
+define([
+  'components/navigator/workspace-header-view',
+  './templates'
+], function (WorkspaceHeaderView) {
+
+  var $ = jQuery;
+
+  return WorkspaceHeaderView.extend({
+    template: Templates['issues-workspace-header'],
+
+    events: function () {
+      return _.extend(WorkspaceHeaderView.prototype.events.apply(this, arguments), {
+        'click .js-back': 'returnToList',
+        'click .js-new-search': 'newSearch'
+      });
+    },
+
+    initialize: function () {
+      var that = this;
+      WorkspaceHeaderView.prototype.initialize.apply(this, arguments);
+      this._onBulkIssues = window.onBulkIssues;
+      window.onBulkIssues = function () {
+        $('#modal').dialog('close');
+        return that.options.app.controller.fetchList();
+      };
+    },
+
+    onClose: function () {
+      window.onBulkIssues = this._onBulkIssues;
+    },
+
+    returnToList: function () {
+      this.options.app.controller.closeComponentViewer();
+    },
+
+    newSearch: function () {
+      this.options.app.controller.newSearch();
+    },
+
+    bulkChange: function () {
+      var query = this.options.app.controller.getQuery('&', true),
+          url = baseUrl + '/issues/bulk_change_form?' + query;
+      window.openModalWindow(url, {});
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js
new file mode 100644 (file)
index 0000000..408900f
--- /dev/null
@@ -0,0 +1,160 @@
+define([
+  'widgets/issue-filter/widget',
+  './templates'
+], function (IssueFilter) {
+
+  var $ = jQuery;
+
+  Handlebars.registerHelper('issuesHomeLink', function (property, value) {
+    return baseUrl + '/issues/search#resolved=false|createdInLast=1w|' +
+        property + '=' + (encodeURIComponent(value));
+  });
+
+  Handlebars.registerHelper('myIssuesHomeLink', function (property, value) {
+    return baseUrl + '/issues/search#resolved=false|createdInLast=1w|assignees=__me__|' +
+        property + '=' + (encodeURIComponent(value));
+  });
+
+  Handlebars.registerHelper('issueFilterHomeLink', function (id) {
+    return baseUrl + '/issues/search#id=' + id;
+  });
+
+  return Marionette.ItemView.extend({
+    template: Templates['issues-workspace-home'],
+
+    modelEvents: {
+      'change': 'render'
+    },
+
+    events: {
+      'click .js-barchart rect': 'selectBar',
+      'click .js-my-barchart rect': 'selectMyBar'
+    },
+
+    initialize: function () {
+      this.model = new Backbone.Model();
+      this.requestIssues();
+      this.requestMyIssues();
+    },
+
+    _getProjects: function (r) {
+      var projectFacet = _.findWhere(r.facets, { property: 'projectUuids' });
+      if (projectFacet != null) {
+        var values = _.head(projectFacet.values, 3);
+        values.forEach(function (v) {
+          var project = _.findWhere(r.projects, { uuid: v.val });
+          v.label = project.longName;
+        });
+        return values;
+      }
+    },
+
+    _getAuthors: function (r) {
+      var authorFacet = _.findWhere(r.facets, { property: 'authors' });
+      if (authorFacet != null) {
+        return _.head(authorFacet.values, 3);
+      }
+    },
+
+    _getTags: function (r) {
+      var MIN_SIZE = 10,
+          MAX_SIZE = 24,
+          tagFacet = _.findWhere(r.facets, { property: 'tags' });
+      if (tagFacet != null) {
+        var values = _.head(tagFacet.values, 10),
+            minCount = _.min(values, function (v) {
+              return v.count;
+            }).count,
+            maxCount = _.max(values, function (v) {
+              return v.count;
+            }).count,
+            scale = d3.scale.linear().domain([minCount, maxCount]).range([MIN_SIZE, MAX_SIZE]);
+        values.forEach(function (v) {
+          v.size = scale(v.count);
+        });
+        return values;
+      }
+    },
+
+    requestIssues: function () {
+      var that = this;
+      var url = baseUrl + '/api/issues/search',
+          options = {
+            resolved: false,
+            createdInLast: '1w',
+            ps: 1,
+            facets: 'createdAt,projectUuids,authors,tags'
+          };
+      return $.get(url, options).done(function (r) {
+        var createdAt = _.findWhere(r.facets, { property: 'createdAt' });
+        that.model.set({
+          createdAt: createdAt != null ? createdAt.values : null,
+          projects: that._getProjects(r),
+          authors: that._getAuthors(r),
+          tags: that._getTags(r)
+        });
+      });
+    },
+
+    requestMyIssues: function () {
+      var that = this;
+      var url = baseUrl + '/api/issues/search',
+          options = {
+            resolved: false,
+            createdInLast: '1w',
+            assignees: '__me__',
+            ps: 1,
+            facets: 'createdAt,projectUuids,authors,tags'
+          };
+      return $.get(url, options).done(function (r) {
+        var createdAt = _.findWhere(r.facets, { property: 'createdAt' });
+        return that.model.set({
+          myCreatedAt: createdAt != null ? createdAt.values : null,
+          myProjects: that._getProjects(r),
+          myTags: that._getTags(r)
+        });
+      });
+    },
+
+    onRender: function () {
+      var values = this.model.get('createdAt'),
+          myValues = this.model.get('myCreatedAt');
+      if (values != null) {
+        this.$('.js-barchart').barchart(values);
+      }
+      if (myValues != null) {
+        this.$('.js-my-barchart').barchart(myValues);
+      }
+      this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
+    },
+
+    selectBar: function (e) {
+      var periodStart = $(e.currentTarget).data('period-start'),
+          periodEnd = $(e.currentTarget).data('period-end');
+      this.options.app.state.setQuery({
+        resolved: false,
+        createdAfter: periodStart,
+        createdBefore: periodEnd
+      });
+    },
+
+    selectMyBar: function (e) {
+      var periodStart = $(e.currentTarget).data('period-start'),
+          periodEnd = $(e.currentTarget).data('period-end');
+      this.options.app.state.setQuery({
+        resolved: false,
+        assignees: '__me__',
+        createdAfter: periodStart,
+        createdBefore: periodEnd
+      });
+    },
+
+    serializeData: function () {
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        user: window.SS.user,
+        filters: _.sortBy(this.options.app.filters.toJSON(), 'name')
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-empty-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-empty-view.js
new file mode 100644 (file)
index 0000000..ada57f1
--- /dev/null
@@ -0,0 +1,11 @@
+define(function () {
+
+  return Marionette.ItemView.extend({
+    className: 'search-navigator-no-results',
+
+    template: function () {
+      return t('issue_filter.no_issues');
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-item-view.js
new file mode 100644 (file)
index 0000000..6d3df9c
--- /dev/null
@@ -0,0 +1,113 @@
+define([
+  'components/issue/issue-view',
+  './issue-filter-view',
+  './templates'
+], function (IssueView, IssueFilterView) {
+
+  var $ = jQuery,
+      SHOULD_NULL = {
+        any: ['issues'],
+        resolutions: ['resolved'],
+        resolved: ['resolutions'],
+        assignees: ['assigned'],
+        assigned: ['assignees'],
+        actionPlans: ['planned'],
+        planned: ['actionPlans']
+      };
+
+  return IssueView.extend({
+    filterTemplate: Templates['issues-issue-filter'],
+
+    events: function () {
+      return _.extend(IssueView.prototype.events.apply(this, arguments), {
+        'click': 'selectCurrent',
+        'dblclick': 'openComponentViewer',
+        'click .js-issue-navigate': 'openComponentViewer',
+        'click .js-issue-filter': 'onIssueFilterClick'
+      });
+    },
+
+    initialize: function (options) {
+      IssueView.prototype.initialize.apply(this, arguments);
+      this.listenTo(options.app.state, 'change:selectedIndex', this.select);
+    },
+
+    onRender: function () {
+      IssueView.prototype.onRender.apply(this, arguments);
+      this.select();
+      this.addFilterSelect();
+      this.$el.addClass('issue-navigate-right');
+    },
+
+    onIssueFilterClick: function (e) {
+      var that = this;
+      e.preventDefault();
+      e.stopPropagation();
+      $('body').click();
+      this.popup = new IssueFilterView({
+        triggerEl: $(e.currentTarget),
+        bottomRight: true,
+        model: this.model
+      });
+      this.popup.on('select', function (property, value) {
+        var obj;
+        obj = {};
+        obj[property] = '' + value;
+        SHOULD_NULL.any.forEach(function (p) {
+          obj[p] = null;
+        });
+        if (SHOULD_NULL[property] != null) {
+          SHOULD_NULL[property].forEach(function (p) {
+            obj[p] = null;
+          });
+        }
+        that.options.app.state.updateFilter(obj);
+        that.popup.close();
+      });
+      this.popup.render();
+    },
+
+    addFilterSelect: function () {
+      this.$('.issue-table-meta-cell-first')
+          .find('.issue-meta-list')
+          .append(this.filterTemplate(this.model.toJSON()));
+    },
+
+    select: function () {
+      var selected = this.model.get('index') === this.options.app.state.get('selectedIndex');
+      this.$el.toggleClass('selected', selected);
+    },
+
+    selectCurrent: function () {
+      this.options.app.state.set({ selectedIndex: this.model.get('index') });
+    },
+
+    resetIssue: function (options) {
+      var that = this;
+      var key = this.model.get('key'),
+          componentUuid = this.model.get('componentUuid'),
+          index = this.model.get('index');
+      this.model.clear({ silent: true });
+      this.model.set({ key: key, componentUuid: componentUuid, index: index }, { silent: true });
+      return this.model.fetch(options).done(function () {
+        return that.trigger('reset');
+      });
+    },
+
+    openComponentViewer: function () {
+      this.options.app.state.set({ selectedIndex: this.model.get('index') });
+      if (this.options.app.state.has('component')) {
+        return this.options.app.controller.closeComponentViewer();
+      } else {
+        return this.options.app.controller.showComponentViewer(this.model);
+      }
+    },
+
+    serializeData: function () {
+      return _.extend(IssueView.prototype.serializeData.apply(this, arguments), {
+        showComponent: true
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
new file mode 100644 (file)
index 0000000..37e7ade
--- /dev/null
@@ -0,0 +1,106 @@
+define([
+  'components/navigator/workspace-list-view',
+  './workspace-list-item-view',
+  './workspace-list-empty-view',
+  './templates'
+], function (WorkspaceListView, IssueView, EmptyView) {
+
+  var $ = jQuery,
+      COMPONENT_HEIGHT = 29,
+      BOTTOM_OFFSET = 10;
+
+  return WorkspaceListView.extend({
+    template: Templates['issues-workspace-list'],
+    componentTemplate: Templates['issues-workspace-list-component'],
+    itemView: IssueView,
+    itemViewContainer: '.js-list',
+    emptyView: EmptyView,
+
+    bindShortcuts: function () {
+      var that = this;
+      var doAction = function (action) {
+        var selectedIssue = that.collection.at(that.options.app.state.get('selectedIndex'));
+        if (selectedIssue == null) {
+          return;
+        }
+        var selectedIssueView = that.children.findByModel(selectedIssue);
+        selectedIssueView.$('.js-issue-' + action).click();
+      };
+      WorkspaceListView.prototype.bindShortcuts.apply(this, arguments);
+      key('right', 'list', function () {
+        var selectedIssue = that.collection.at(that.options.app.state.get('selectedIndex'));
+        that.options.app.controller.showComponentViewer(selectedIssue);
+        return false;
+      });
+      key('f', 'list', function () {
+        return doAction('transition');
+      });
+      key('a', 'list', function () {
+        return doAction('assign');
+      });
+      key('m', 'list', function () {
+        return doAction('assign-to-me');
+      });
+      key('p', 'list', function () {
+        return doAction('plan');
+      });
+      key('i', 'list', function () {
+        return doAction('set-severity');
+      });
+      key('c', 'list', function () {
+        return doAction('comment');
+      });
+      return key('t', 'list', function () {
+        return doAction('edit-tags');
+      });
+    },
+
+    scrollTo: function () {
+      var selectedIssue = this.collection.at(this.options.app.state.get('selectedIndex'));
+      if (selectedIssue == null) {
+        return;
+      }
+      var selectedIssueView = this.children.findByModel(selectedIssue),
+          parentTopOffset = this.$el.offset().top,
+          viewTop = selectedIssueView.$el.offset().top - parentTopOffset;
+      if (selectedIssueView.$el.prev().is('.issues-workspace-list-component')) {
+        viewTop -= COMPONENT_HEIGHT;
+      }
+      var viewBottom = selectedIssueView.$el.offset().top + selectedIssueView.$el.outerHeight() + BOTTOM_OFFSET,
+          windowTop = $(window).scrollTop(),
+          windowBottom = windowTop + $(window).height();
+      if (viewTop < windowTop) {
+        $(window).scrollTop(viewTop);
+      }
+      if (viewBottom > windowBottom) {
+        $(window).scrollTop($(window).scrollTop() - windowBottom + viewBottom);
+      }
+    },
+
+    appendHtml: function (compositeView, itemView, index) {
+      var $container = this.getItemViewContainer(compositeView),
+          model = this.collection.at(index);
+      if (model != null) {
+        var prev = this.collection.at(index - 1),
+            putComponent = prev == null;
+        if (prev != null) {
+          var fullComponent = [model.get('project'), model.get('component')].join(' '),
+              fullPrevComponent = [prev.get('project'), prev.get('component')].join(' ');
+          if (fullComponent !== fullPrevComponent) {
+            putComponent = true;
+          }
+        }
+        if (putComponent) {
+          $container.append(this.componentTemplate(model.toJSON()));
+        }
+      }
+      $container.append(itemView.el);
+    },
+
+    closeChildren: function () {
+      WorkspaceListView.prototype.closeChildren.apply(this, arguments);
+      this.$('.issues-workspace-list-component').remove();
+    }
+  });
+
+});