aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/coffee/components/issue/views
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-05-18 16:29:19 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-05-19 18:20:09 +0200
commite59e4aa2c06a57566f148f4217d286461231ae58 (patch)
treead0918eb06b2474a6c71997b7ff573bbb93e0170 /server/sonar-web/src/main/coffee/components/issue/views
parent8d82c9cf9ba88c1517604b4063ad0fc2a9a1bb9a (diff)
downloadsonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.tar.gz
sonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.zip
change web structure: separate components
Diffstat (limited to 'server/sonar-web/src/main/coffee/components/issue/views')
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee120
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee148
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee38
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee84
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee48
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee41
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee78
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee65
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee161
-rw-r--r--server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee53
10 files changed, 836 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee
new file mode 100644
index 00000000000..878d172f09f
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/action-options-view.coffee
@@ -0,0 +1,120 @@
+#
+# 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/common/popup'
+], (
+ PopupView
+) ->
+
+ $ = jQuery
+
+
+ class extends PopupView
+ keyScope: 'issue-action-options'
+
+
+ ui:
+ options: '.issue-action-option'
+
+
+ events: ->
+ 'click .issue-action-option': 'selectOption'
+ 'mouseenter .issue-action-option': 'activateOptionByPointer'
+
+
+ initialize: ->
+ @bindShortcuts()
+
+
+ onRender: ->
+ super
+ @selectInitialOption()
+ @$('[data-toggle="tooltip"]').tooltip container: 'body'
+
+
+ getOptions: ->
+ @$('.issue-action-option')
+
+
+ getActiveOption: ->
+ @getOptions().filter('.active')
+
+
+ makeActive: (option) ->
+ if option.length > 0
+ @getOptions().removeClass 'active'
+ option.addClass 'active'
+
+
+ selectInitialOption: ->
+ @makeActive @getOptions().first()
+
+
+ selectNextOption: ->
+ @makeActive @getActiveOption().nextAll('.issue-action-option').first()
+ false # return `false` to use with keymaster
+
+
+ selectPreviousOption: ->
+ @makeActive @getActiveOption().prevAll('.issue-action-option').first()
+ false # return `false` to use with keymaster
+
+
+ activateOptionByPointer: (e) ->
+ @makeActive $(e.currentTarget)
+
+
+ bindShortcuts: ->
+ @currentKeyScope = key.getScope()
+ key.setScope @keyScope
+ key 'down', @keyScope, => @selectNextOption()
+ key 'up', @keyScope, => @selectPreviousOption()
+ key 'return', @keyScope, => @selectActiveOption()
+ key 'escape', @keyScope, => @close()
+ key 'backspace', @keyScope, => false # disable go back through the history
+ key 'shift+tab', @keyScope, => false
+
+
+ unbindShortcuts: ->
+ key.unbind 'down', @keyScope
+ key.unbind 'up', @keyScope
+ key.unbind 'return', @keyScope
+ key.unbind 'escape', @keyScope
+ key.unbind 'backspace', @keyScope
+ key.unbind 'tab', @keyScope
+ key.unbind 'shift+tab', @keyScope
+ key.setScope @currentKeyScope
+
+
+ onClose: ->
+ super
+ @unbindShortcuts()
+ @$('[data-toggle="tooltip"]').tooltip 'destroy'
+ $('.tooltip').remove()
+
+
+ selectOption: (e) ->
+ e.preventDefault()
+ @close()
+
+
+ selectActiveOption: ->
+ @getActiveOption().click()
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee
new file mode 100644
index 00000000000..ae00ec881a3
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/assign-form-view.coffee
@@ -0,0 +1,148 @@
+#
+# 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 [
+ './action-options-view'
+ '../templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issue-assign-form']
+ optionTemplate: Templates['issue-assign-form-option']
+
+
+ events: ->
+ _.extend super,
+ 'click input': 'onInputClick'
+ 'keydown input': 'onInputKeydown'
+ 'keyup input': 'onInputKeyup'
+
+
+ initialize: ->
+ super
+ @assignees = []
+ @debouncedSearch = _.debounce @search, 250
+
+
+ getAssignee: ->
+ @model.get 'assignee'
+
+
+ getAssigneeName: ->
+ @model.get 'assigneeName'
+
+
+ onRender: ->
+ super
+ @renderTags()
+ setTimeout (=> @$('input').focus()), 100
+
+
+ renderTags: ->
+ @$('.issue-action-option').remove()
+ @getAssignees().forEach @renderAssignee, @
+ @selectInitialOption()
+
+
+ renderAssignee: (assignee) ->
+ html = @optionTemplate assignee
+ @$('.issue-action-options').append html
+
+
+ selectOption: (e) ->
+ assignee = $(e.currentTarget).data 'value'
+ assigneeName = $(e.currentTarget).data 'text'
+ @submit assignee, assigneeName
+ super
+
+
+ submit: (assignee, assigneeName) ->
+ _assignee = @getAssignee()
+ _assigneeName = @getAssigneeName()
+ return if assignee == _assignee
+ if assignee == ''
+ @model.set assignee: null, assigneeName: null
+ else
+ @model.set assignee: assignee, assigneeName: assigneeName
+ $.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/issues/assign"
+ data:
+ issue: @model.id
+ assignee: assignee
+ .fail =>
+ @model.set assignee: _assignee, assigneeName: _assigneeName
+
+
+ onInputClick: (e) ->
+ e.stopPropagation()
+
+
+ onInputKeydown: (e) ->
+ @query = @$('input').val()
+ return @selectPreviousOption() if e.keyCode == 38 # up
+ return @selectNextOption() if e.keyCode == 40 # down
+ return @selectActiveOption() if e.keyCode == 13 # return
+ return false if e.keyCode == 9 # tab
+ @close() if e.keyCode == 27 # escape
+
+
+ onInputKeyup: ->
+ query = @$('input').val()
+ if query != @query
+ query = '' if query.length < 2
+ @query = query
+ @debouncedSearch query
+
+
+ search: (query) ->
+ if query.length > 1
+ $.get "#{baseUrl}/api/users/search", q: query
+ .done (data) =>
+ @resetAssignees data.users
+ else
+ @resetAssignees []
+
+
+ resetAssignees: (users) ->
+ @assignees = users.map (user) ->
+ id: user.login
+ text: user.name
+ @renderTags()
+
+
+ getAssignees: ->
+ return @assignees if @assignees.length > 0
+ assignees = [{ id: '', text: t('unassigned') }]
+ currentUser = window.SS.user
+ currentUserName = window.SS.userName
+ assignees.push id: currentUser, text: currentUserName
+ if @getAssignee()
+ assignees.push id: @getAssignee(), text: @getAssigneeName()
+ @makeUnique assignees
+
+
+ makeUnique: (assignees) ->
+ _.uniq assignees, false, (assignee) -> assignee.id
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee
new file mode 100644
index 00000000000..34e77c9191b
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/changelog-view.coffee
@@ -0,0 +1,38 @@
+#
+# 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/common/popup'
+ '../templates'
+], (
+ PopupView
+) ->
+
+ class extends PopupView
+ template: Templates['issue-changelog']
+
+
+ collectionEvents:
+ 'sync': 'render'
+
+
+ serializeData: ->
+ _.extend super,
+ issue: @options.issue.toJSON()
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee
new file mode 100644
index 00000000000..95fa6739d4b
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/comment-form-view.coffee
@@ -0,0 +1,84 @@
+#
+# 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/common/popup'
+ '../templates'
+], (
+ PopupView
+) ->
+
+ $ = jQuery
+
+
+ class extends PopupView
+ className: 'bubble-popup issue-comment-bubble-popup'
+ template: Templates['comment-form']
+
+
+ ui:
+ textarea: '.issue-comment-form-text textarea'
+ cancelButton: '.js-issue-comment-cancel'
+ submitButton: '.js-issue-comment-submit'
+
+
+ events:
+ 'click': 'onClick'
+ 'keydown @ui.textarea': 'onKeydown'
+ 'keyup @ui.textarea': 'toggleSubmit'
+ 'click @ui.cancelButton': 'cancel'
+ 'click @ui.submitButton': 'submit'
+
+
+ onRender: ->
+ super
+ setTimeout (=> @ui.textarea.focus()), 100
+
+
+ toggleSubmit: ->
+ @ui.submitButton.prop 'disabled', @ui.textarea.val().length == 0
+
+
+ onClick: (e) ->
+ # disable close by clicking inside
+ e.stopPropagation()
+
+
+ onKeydown: (e) ->
+ @close() if e.keyCode == 27 # escape
+
+
+ cancel: ->
+ @options.detailView.updateAfterAction false
+
+
+ submit: ->
+ text = @ui.textarea.val()
+ update = @model && @model.has('key')
+ method = if update then 'edit_comment' else 'add_comment'
+ url = "#{baseUrl}/api/issues/#{method}"
+ data = text: text
+ if update
+ data.key = @model.get('key')
+ else
+ data.issue = @options.issue.id
+ $.post url, data
+ .done =>
+ @options.detailView.updateAfterAction true
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee b/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee
new file mode 100644
index 00000000000..fa54c06c298
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/issue-popup.coffee
@@ -0,0 +1,48 @@
+#
+# 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/common/popup'
+], (
+ Popup
+) ->
+
+ class extends Popup
+ className: 'bubble-popup issue-bubble-popup'
+
+
+ template: -> '<div class="bubble-popup-arrow"></div>'
+
+
+ events: ->
+ 'click .js-issue-form-cancel': 'close'
+
+
+ onRender: ->
+ super
+ @options.view.$el.appendTo @$el
+ @options.view.render()
+
+
+ onClose: ->
+ @options.view.close()
+
+
+ attachCloseEvents: ->
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee
new file mode 100644
index 00000000000..868dbb2b9fb
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/more-actions-view.coffee
@@ -0,0 +1,41 @@
+#
+# 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/common/popup'
+ '../templates'
+], (
+ PopupView
+) ->
+
+ $ = jQuery
+
+
+ class extends PopupView
+ template: Templates['issue-more-actions']
+
+
+ events: ->
+ 'click .js-issue-action': 'action'
+
+
+ action: (e) ->
+ actionKey = $(e.currentTarget).data 'action'
+ @options.detailView.action actionKey
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee
new file mode 100644
index 00000000000..bface05c07e
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/plan-form-view.coffee
@@ -0,0 +1,78 @@
+#
+# 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 [
+ './action-options-view'
+ '../templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issue-plan-form']
+
+
+ getActionPlan: ->
+ @model.get('actionPlan') || ''
+
+
+ getActionPlanName: ->
+ @model.get 'actionPlanName'
+
+
+ selectInitialOption: ->
+ @makeActive @getOptions().filter("[data-value=#{@getActionPlan()}]")
+
+
+ selectOption: (e) ->
+ actionPlan = $(e.currentTarget).data 'value'
+ actionPlanName = $(e.currentTarget).data 'text'
+ @submit actionPlan, actionPlanName
+ super
+
+
+ submit: (actionPlan, actionPlanName) ->
+ _actionPlan = @getActionPlan()
+ _actionPlanName = @getActionPlanName()
+ return if actionPlan == _actionPlan
+ if actionPlan == ''
+ @model.set actionPlan: null, actionPlanName: null
+ else
+ @model.set actionPlan: actionPlan, actionPlanName: actionPlanName
+ $.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/issues/plan"
+ data:
+ issue: @model.id
+ plan: actionPlan
+ .fail =>
+ @model.set assignee: _actionPlan, assigneeName: _actionPlanName
+
+
+ getActionPlans: ->
+ [{ key: '', name: t 'issue.unplanned' }].concat @collection.toJSON()
+
+
+ serializeData: ->
+ _.extend super,
+ items: @getActionPlans()
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee
new file mode 100644
index 00000000000..bdd813ff449
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/set-severity-form-view.coffee
@@ -0,0 +1,65 @@
+#
+# 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 [
+ './action-options-view'
+ '../templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issue-set-severity-form']
+
+
+ getTransition: ->
+ @model.get 'severity'
+
+
+ selectInitialOption: ->
+ @makeActive @getOptions().filter("[data-value=#{@getTransition()}]")
+
+
+ selectOption: (e) ->
+ severity = $(e.currentTarget).data 'value'
+ @submit severity
+ super
+
+
+ submit: (severity) ->
+ _severity = @getTransition()
+ return if severity == _severity
+ @model.set severity: severity
+ $.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/issues/set_severity"
+ data:
+ issue: @model.id
+ severity: severity
+ .fail =>
+ @model.set severity: _severity
+
+
+ serializeData: ->
+ _.extend super,
+ items: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee
new file mode 100644
index 00000000000..ea08c2c54fd
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/tags-form-view.coffee
@@ -0,0 +1,161 @@
+#
+# 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 [
+ './action-options-view'
+ '../templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issue-tags-form']
+ optionTemplate: Templates['issue-tags-form-option']
+
+
+ modelEvents:
+ 'change:tags': 'renderTags'
+
+
+ events: ->
+ _.extend super,
+ 'click input': 'onInputClick'
+ 'keydown input': 'onInputKeydown'
+ 'keyup input': 'onInputKeyup'
+
+
+ initialize: ->
+ super
+ @query = ''
+ @tags = []
+ @selected = 0
+ @debouncedSearch = _.debounce @search, 250
+ @requestTags()
+
+
+ requestTags: ->
+ $.get "#{baseUrl}/api/issues/tags", ps: 25
+ .done (data) =>
+ @tags = data.tags
+ @renderTags()
+
+
+ onRender: ->
+ super
+ @renderTags()
+ setTimeout (=> @$('input').focus()), 100
+
+
+ selectInitialOption: ->
+ @selected = Math.max Math.min(@selected, @getOptions().length - 1), 0
+ @makeActive @getOptions().eq @selected
+
+
+ filterTags: (tags) ->
+ _.filter tags, (tag) => tag.indexOf(@query) != -1
+
+
+ renderTags: ->
+ @$('.issue-action-option').remove()
+ @filterTags(@getTags()).forEach @renderSelectedTag, @
+ @filterTags(_.difference(@tags, @getTags())).forEach @renderTag, @
+ if @query.length > 0 && @tags.indexOf(@query) == -1 && @getTags().indexOf(@query) == -1
+ @renderCustomTag @query
+ @selectInitialOption()
+
+
+ renderSelectedTag: (tag) ->
+ html = @optionTemplate { tag: tag, selected: true, custom: false }
+ @$('.issue-action-options').append html
+
+
+ renderTag: (tag) ->
+ html = @optionTemplate { tag: tag, selected: false, custom: false }
+ @$('.issue-action-options').append html
+
+
+ renderCustomTag: (tag) ->
+ html = @optionTemplate { tag: tag, selected: false, custom: true }
+ @$('.issue-action-options').append html
+
+
+ selectOption: (e) ->
+ e.preventDefault()
+ e.stopPropagation()
+ tags = @getTags().slice()
+ tag = $(e.currentTarget).data 'value'
+ if $(e.currentTarget).data('selected')?
+ tags = _.without tags, tag
+ else
+ tags.push tag
+ @selected = @getOptions().index $(e.currentTarget)
+ @submit tags
+
+
+ submit: (tags) ->
+ _tags = @getTags()
+ @model.set tags: tags
+ $.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/issues/set_tags"
+ data:
+ key: @model.id
+ tags: tags.join()
+ .fail =>
+ @model.set tags: _tags
+
+
+ onInputClick: (e) ->
+ e.stopPropagation()
+
+
+ onInputKeydown: (e) ->
+ @query = @$('input').val()
+ return @selectPreviousOption() if e.keyCode == 38 # up
+ return @selectNextOption() if e.keyCode == 40 # down
+ return @selectActiveOption() if e.keyCode == 13 # return
+ return false if e.keyCode == 9 # tab
+ @close() if e.keyCode == 27 # escape
+
+
+ onInputKeyup: ->
+ query = @$('input').val()
+ if query != @query
+ @query = query
+ @debouncedSearch query
+
+
+ search: (query) ->
+ @query = query
+ @renderTags()
+
+
+ resetAssignees: (users) ->
+ @assignees = users.map (user) ->
+ id: user.login
+ text: user.name
+ @renderTags()
+
+
+ getTags: ->
+ @model.get('tags') || []
diff --git a/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee b/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee
new file mode 100644
index 00000000000..cf4d47d8592
--- /dev/null
+++ b/server/sonar-web/src/main/coffee/components/issue/views/transitions-form-view.coffee
@@ -0,0 +1,53 @@
+#
+# 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 [
+ './action-options-view'
+ '../templates'
+], (
+ ActionOptionsView
+) ->
+
+ $ = jQuery
+
+
+ class extends ActionOptionsView
+ template: Templates['issue-transitions-form']
+
+
+ selectInitialOption: ->
+ @makeActive @getOptions().first()
+
+
+ selectOption: (e) ->
+ transition = $(e.currentTarget).data 'value'
+ @submit transition
+ super
+
+
+ submit: (transition) ->
+ $.ajax
+ type: 'POST',
+ url: baseUrl + '/api/issues/do_transition',
+ data:
+ issue: @model.get('key')
+ transition: transition
+ .done =>
+ @options.view.resetIssue {}