]
- coffee:
- build:
- files: [
- expand: true
- cwd: '<%= SOURCE_PATH %>/coffee'
- src: ['**/*.coffee']
- dest: '<%= BUILD_PATH %>/js'
- ext: '.js'
- ]
-
-
concat:
build:
files:
'<%= 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'
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']
# 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']
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
-
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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 } }
+++ /dev/null
-#
-# 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'
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
-
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
-
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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'
+++ /dev/null
-#
-# 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'
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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'
- }
-
+++ /dev/null
-#
-# 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
+++ /dev/null
-{{#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}}
+++ /dev/null
-<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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-<div class="search-navigator-facet-query">
- Issues of {{qualifierIcon state.contextComponentQualifier}} {{state.contextComponentName}}
-</div>
+++ /dev/null
-{{> '_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}} <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}} <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}}
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-{{> '_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>
+++ /dev/null
-<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>
+++ /dev/null
-<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}} {{t 'severity' severity}}
- </a>
-
- <a href="#" class="issue-action-option" data-property="statuses" data-value="{{status}}">
- {{statusIcon status}} {{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> {{this}}
- </a>
- {{/each}}
-
- <hr>
-
- <a href="#" class="issue-action-option" data-property="projectUuids" data-value="{{projectUuid}}">
- {{qualifierIcon 'TRK'}} {{projectLongName}}
- </a>
-
- {{#if subProject}}
- <a href="#" class="issue-action-option" data-property="moduleUuids" data-value="{{subProjectUuid}}">
- {{qualifierIcon 'BRC'}} {{subProjectLongName}}
- </a>
- {{/if}}
-
- <a href="#" class="issue-action-option" data-property="fileUuids" data-value="{{componentUuid}}">
- {{qualifierIcon componentQualifier}} {{fileFromPath componentLongName}}
- </a>
-</div>
-
-<div class="bubble-popup-arrow"></div>
+++ /dev/null
-<div class="issue-meta">
- <a class="issue-action issue-action-with-options js-issue-filter" href="#">
- <i class="icon-filter icon-half-transparent"></i> <i class="icon-dropdown"></i>
- </a>
-</div>
+++ /dev/null
-<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>
+++ /dev/null
-<div class="issues-header-component nowrap">
- {{#if state.component}}
- <a class="js-back">{{t 'issues.return_to_list'}}</a>
-
- {{#with state.component}}
- {{qualifierIcon 'TRK'}} <a href="{{dashboardUrl project}}" title="{{projectName}}">{{projectName}}</a>
-
- {{qualifierIcon qualifier}} <a href="{{dashboardUrl key}}" title="{{name}}">{{name}}</a>
- {{/with}}
- {{else}}
-
- {{/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>
+++ /dev/null
-<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'}} <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'}} <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>
+++ /dev/null
-<div class="issues-workspace-list-component">
- <a class="issues-workspace-list-component-part" href="{{dashboardUrl project}}">
- {{qualifierIcon 'TRK'}} {{projectLongName}}
- </a>
- {{#if subProject}}
- <a class="issues-workspace-list-component-part" href="{{dashboardUrl subProject}}">
- {{qualifierIcon 'TRK'}} {{subProjectLongName}}
- </a>
- {{/if}}
- <a class="issues-workspace-list-component-part" href="{{dashboardUrl component}}">
- {{qualifierIcon componentQualifier}} {{componentLongName}}
- </a>
-</div>
+++ /dev/null
-<div class="js-list"></div>
-
-<div class="search-navigator-workspace-list-more js-more">
- <i class="spinner"></i>
-</div>
+++ /dev/null
-#
-# 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, {}
+++ /dev/null
-#
-# 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'
+++ /dev/null
-#
-# 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'
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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()
--- /dev/null
+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();
+ });
+
+});
--- /dev/null
+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();
+ });
+
+});
--- /dev/null
+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
+ });
+ }
+ });
+
+});
--- /dev/null
+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();
+ }
+ });
+
+});
--- /dev/null
+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();
+ }
+ });
+
+});
--- /dev/null
+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;
+ }
+ }
+ });
+
+});
--- /dev/null
+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()
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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 };
+ })
+ };
+ }
+ }
+ });
+ }
+ });
+
+});
--- /dev/null
+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');
+ }
+ });
+
+});
--- /dev/null
+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()
+ });
+ }
+ });
+
+});
--- /dev/null
+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
+ });
+ }
+ });
+
+});
--- /dev/null
+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);
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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);
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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);
+ });
+ }
+ });
+
+});
--- /dev/null
+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);
+ });
+ }
+ });
+
+});
--- /dev/null
+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())
+ });
+ }
+ });
+
+});
--- /dev/null
+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
+ });
+ }
+ });
+
+});
--- /dev/null
+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')
+ });
+ }
+ });
+
+});
--- /dev/null
+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');
+ }
+ });
+
+});
--- /dev/null
+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 });
+ }
+ });
+
+});
--- /dev/null
+define([
+ './facet'
+], function (Facet) {
+
+ return Backbone.Collection.extend({
+ model: Facet
+ });
+
+});
--- /dev/null
+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;
+ }
+ }
+ });
+
+});
--- /dev/null
+define([
+ './filter'
+], function (Filter) {
+
+ return Backbone.Collection.extend({
+ model: Filter
+ });
+
+});
--- /dev/null
+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 });
+ });
+ }
+ });
+
+});
--- /dev/null
+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'
+ }
+ }
+ });
+
+});
--- /dev/null
+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);
+ }
+ }
+ });
+
+});
--- /dev/null
+{{#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}}
--- /dev/null
+<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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+<div class='search-navigator-facet-query'>
+ Issues of {{qualifierIcon state.contextComponentQualifier}} {{state.contextComponentName}}
+</div>
--- /dev/null
+{{> '_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}} <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}} <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}}
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+{{> '_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>
--- /dev/null
+<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>
--- /dev/null
+<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}} {{t 'severity' severity}}
+ </a>
+
+ <a href='#' class='issue-action-option' data-property='statuses' data-value='{{status}}'>
+ {{statusIcon status}} {{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> {{this}}
+ </a>
+ {{/each}}
+
+ <hr>
+
+ <a href='#' class='issue-action-option' data-property='projectUuids' data-value='{{projectUuid}}'>
+ {{qualifierIcon 'TRK'}} {{projectLongName}}
+ </a>
+
+ {{#if subProject}}
+ <a href='#' class='issue-action-option' data-property='moduleUuids' data-value='{{subProjectUuid}}'>
+ {{qualifierIcon 'BRC'}} {{subProjectLongName}}
+ </a>
+ {{/if}}
+
+ <a href='#' class='issue-action-option' data-property='fileUuids' data-value='{{componentUuid}}'>
+ {{qualifierIcon componentQualifier}} {{fileFromPath componentLongName}}
+ </a>
+</div>
+
+<div class='bubble-popup-arrow'></div>
--- /dev/null
+<div class='issue-meta'>
+ <a class='issue-action issue-action-with-options js-issue-filter' href='#'>
+ <i class='icon-filter icon-half-transparent'></i> <i class='icon-dropdown'></i>
+ </a>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<div class='issues-header-component nowrap'>
+ {{#if state.component}}
+ <a class='js-back'>{{t 'issues.return_to_list'}}</a>
+
+ {{#with state.component}}
+ {{qualifierIcon 'TRK'}} <a href='{{dashboardUrl project}}' title='{{projectName}}'>{{projectName}}</a>
+
+ {{qualifierIcon qualifier}} <a href='{{dashboardUrl key}}' title='{{name}}'>{{name}}</a>
+ {{/with}}
+ {{else}}
+
+ {{/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>
--- /dev/null
+<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'}} <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'}} <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>
--- /dev/null
+<div class='issues-workspace-list-component'>
+ <a class='issues-workspace-list-component-part' href='{{dashboardUrl project}}'>
+ {{qualifierIcon 'TRK'}} {{projectLongName}}
+ </a>
+ {{#if subProject}}
+ <a class='issues-workspace-list-component-part' href='{{dashboardUrl subProject}}'>
+ {{qualifierIcon 'TRK'}} {{subProjectLongName}}
+ </a>
+ {{/if}}
+ <a class='issues-workspace-list-component-part' href='{{dashboardUrl component}}'>
+ {{qualifierIcon componentQualifier}} {{componentLongName}}
+ </a>
+</div>
--- /dev/null
+<div class='js-list'></div>
+
+<div class='search-navigator-workspace-list-more js-more'>
+ <i class='spinner'></i>
+</div>
--- /dev/null
+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, {});
+ }
+ });
+
+});
--- /dev/null
+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')
+ });
+ }
+ });
+
+});
--- /dev/null
+define(function () {
+
+ return Marionette.ItemView.extend({
+ className: 'search-navigator-no-results',
+
+ template: function () {
+ return t('issue_filter.no_issues');
+ }
+ });
+
+});
--- /dev/null
+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
+ });
+ }
+ });
+
+});
--- /dev/null
+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();
+ }
+ });
+
+});