From 1165e53e494a06ff39eee1d4a716ef170caa2276 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 3 Feb 2015 15:51:57 +0100 Subject: [PATCH] SONAR-5946 SONAR-6027 better handling of ajax errors --- server/sonar-web/.jshintrc | 2 + .../main/coffee/analysis-reports/app.coffee | 5 - .../src/main/coffee/common/processes.coffee | 67 ------- .../src/main/coffee/issue/issue-view.coffee | 28 +-- .../issue/views/assign-form-view.coffee | 8 - .../issue/views/comment-form-view.coffee | 4 - .../coffee/issue/views/plan-form-view.coffee | 4 - .../issue/views/set-severity-form-view.coffee | 4 - .../coffee/issue/views/tags-form-view.coffee | 4 - .../issue/views/transitions-form-view.coffee | 5 +- .../src/main/coffee/issues/app-context.coffee | 2 - .../src/main/coffee/issues/app-new.coffee | 2 - .../src/main/coffee/issues/controller.coffee | 4 - .../issues/workspace-list-item-view.coffee | 5 +- .../src/main/coffee/quality-gate/app.coffee | 15 -- .../sonar-web/src/main/js/coding-rules/app.js | 4 +- .../js/coding-rules/bulk-change-modal-view.js | 4 - .../src/main/js/coding-rules/controller.js | 6 +- .../main/js/coding-rules/rule-details-view.js | 6 +- .../js/coding-rules/rule/custom-rule-view.js | 6 +- .../rule/manual-rule-creation-view.js | 3 - .../rule/profile-activation-view.js | 3 - .../rule/rule-description-view.js | 5 +- .../js/coding-rules/rule/rule-meta-view.js | 3 - .../js/coding-rules/rule/rule-profile-view.js | 4 - .../src/main/js/coding-rules/show-app.js | 6 +- .../coding-rules/workspace-list-item-view.js | 2 - .../sonar-web/src/main/js/common/processes.js | 163 ++++++++++++++++++ .../js/components/navigator/controller.js | 5 - .../src/main/js/nav/context-navbar-view.js | 6 +- .../sonar-web/src/main/js/nav/search-view.js | 6 +- .../main/js/source-viewer/measures-overlay.js | 6 - .../src/main/js/source-viewer/viewer.js | 8 +- server/sonar-web/src/main/less/ui.less | 28 ++- 34 files changed, 197 insertions(+), 236 deletions(-) delete mode 100644 server/sonar-web/src/main/coffee/common/processes.coffee create mode 100644 server/sonar-web/src/main/js/common/processes.js diff --git a/server/sonar-web/.jshintrc b/server/sonar-web/.jshintrc index 2d6820a4d4c..5b2eb1b387b 100644 --- a/server/sonar-web/.jshintrc +++ b/server/sonar-web/.jshintrc @@ -82,7 +82,9 @@ "baseUrl": true, "key": true, "Backbone": true, + "Marionette": true, "Handlebars": true, + "Templates": true, "t": true, "tp": true, "moment": true, diff --git a/server/sonar-web/src/main/coffee/analysis-reports/app.coffee b/server/sonar-web/src/main/coffee/analysis-reports/app.coffee index 308a8e8ba3a..1fb00a6eb5e 100644 --- a/server/sonar-web/src/main/coffee/analysis-reports/app.coffee +++ b/server/sonar-web/src/main/coffee/analysis-reports/app.coffee @@ -25,7 +25,6 @@ requirejs [ App.fetchReports = -> - process = window.process.addBackgroundProcess() fetch = if @state.get 'active' then @reports.fetchActive() else @reports.fetchHistory() @layout.showSpinner 'actionsRegion' @layout.resultsRegion.reset() @@ -44,18 +43,14 @@ requirejs [ collection: @reports @layout.actionsRegion.show @actionsView - window.process.finishBackgroundProcess process - App.fetchNextPage = -> - process = window.process.addBackgroundProcess() @reports.fetchHistory data: p: @state.get('page') + 1 remove: false .done => @state.set page: @reports.paging.page - window.process.finishBackgroundProcess process App.addInitializer -> diff --git a/server/sonar-web/src/main/coffee/common/processes.coffee b/server/sonar-web/src/main/coffee/common/processes.coffee deleted file mode 100644 index 7f15480eef2..00000000000 --- a/server/sonar-web/src/main/coffee/common/processes.coffee +++ /dev/null @@ -1,67 +0,0 @@ -$ = jQuery - -process = {} -process.queue = {} -process.timeout = 300 -process.fadeTimeout = 100 - -_.extend process, - - addBackgroundProcess: -> - uid = _.uniqueId 'process' - @renderSpinner uid - @queue[uid] = setTimeout (=> @showSpinner uid), @timeout - uid - - - isBackgroundProcessAlive: (uid) -> - @queue[uid]? - - - finishBackgroundProcess: (uid) -> - if @isBackgroundProcessAlive uid - clearInterval @queue[uid] - delete @queue[uid] - @removeSpinner uid - - - failBackgroundProcess: (uid) -> - if @isBackgroundProcessAlive uid - clearInterval @queue[uid] - delete @queue[uid] - spinner = @getSpinner uid - spinner.addClass 'process-spinner-failed shown' - spinner.text t 'process.fail' - close = $('').html('').addClass 'process-spinner-close' - close.appendTo spinner - close.on 'click', => @removeSpinner uid - - - renderSpinner: (uid) -> - id = "spinner-#{uid}" - spinner = $ '
' - spinner.addClass 'process-spinner' - spinner.prop 'id', id - text = t 'process.still_working' - text = 'Still Working...' if text == 'process.still_working' - spinner.text text - spinner.appendTo $('body') - - - showSpinner: (uid) -> - spinner = @getSpinner(uid) - setTimeout (-> spinner.addClass 'shown'), @fadeTimeout - - - removeSpinner: (uid) -> - @getSpinner(uid).remove() - - - getSpinner: (uid) -> - id = "spinner-#{uid}" - $('#' + id) - - -_.extend window, process: process - - diff --git a/server/sonar-web/src/main/coffee/issue/issue-view.coffee b/server/sonar-web/src/main/coffee/issue/issue-view.coffee index 96183201830..622ff88cd98 100644 --- a/server/sonar-web/src/main/coffee/issue/issue-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/issue-view.coffee @@ -78,7 +78,7 @@ define [ @$el.attr 'data-key', @model.get('key') - resetIssue: (options, p) -> + resetIssue: (options) -> key = @model.get 'key' componentUuid = @model.get 'componentUuid' @model.clear silent: true @@ -86,9 +86,6 @@ define [ @model.fetch(options) .done => @trigger 'reset' - window.process.finishBackgroundProcess p if p? - .fail -> - window.process.failBackgroundProcess p if p? showChangeLog: (e) -> @@ -131,9 +128,7 @@ define [ updateAfterAction: (fetch) -> @popup.close() if @popup if fetch - p = window.process.addBackgroundProcess() - $.when(@resetIssue()).done => - window.process.finishBackgroundProcess p + @resetIssue() comment: (e) -> @@ -163,7 +158,6 @@ define [ deleteComment: (e) -> - p = window.process.addBackgroundProcess() commentKey = $(e.target).closest('[data-comment-key]').data 'comment-key' confirmMsg = $(e.target).data 'confirm-msg' @@ -173,9 +167,6 @@ define [ url: baseUrl + "/api/issues/delete_comment?key=" + commentKey .done => @updateAfterAction true - window.process.finishBackgroundProcess p - .fail => - window.process.failBackgroundProcess p transition: (e) -> @@ -216,7 +207,6 @@ define [ plan: (e) -> - p = window.process.addBackgroundProcess() t = $(e.currentTarget) actionPlans = new ActionPlans() actionPlans.fetch @@ -231,9 +221,6 @@ define [ model: @model collection: actionPlans @popup.render() - window.process.finishBackgroundProcess p - .fail => - window.process.failBackgroundProcess p showMoreActions: (e) -> @@ -248,13 +235,9 @@ define [ action: (action) -> - p = window.process.addBackgroundProcess() $.post "#{baseUrl}/api/issues/do_action", issue: @model.id, actionKey: action .done => - window.process.finishBackgroundProcess p @resetIssue() - .fail => - window.process.failBackgroundProcess p showRule: -> @@ -277,11 +260,9 @@ define [ changeTags: -> - p = window.process.addBackgroundProcess() jQuery.ajax url: "#{baseUrl}/api/issues/tags?ps=0" .done (r) => - window.process.finishBackgroundProcess p if @ui.tagInput.select2 # Prevent synchronization issue with navigation @ui.tagInput.select2 @@ -305,8 +286,6 @@ define [ ) @ui.tagInput.select2 'focus' - .fail => - window.process.failBackgroundProcess p cancelEdit: -> @@ -328,15 +307,12 @@ define [ tags = @ui.tagInput.val() splitTags = if tags then tags.split(',') else null - p = window.process.addBackgroundProcess() @model.set 'tags', splitTags $.post "#{baseUrl}/api/issues/set_tags", key: @model.get('key'), tags: tags .done => - window.process.finishBackgroundProcess p @cancelEdit() .fail => @model.set 'tags', _tags - window.process.failBackgroundProcess p .always => @render() diff --git a/server/sonar-web/src/main/coffee/issue/views/assign-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/assign-form-view.coffee index ed4b0379058..71279c6c97c 100644 --- a/server/sonar-web/src/main/coffee/issue/views/assign-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/assign-form-view.coffee @@ -69,7 +69,6 @@ define [ _assignee = @getAssignee() _assigneeName = @getAssigneeName() return if assignee == _assignee - p = window.process.addBackgroundProcess() if assignee == '' @model.set assignee: null, assigneeName: null else @@ -80,11 +79,8 @@ define [ data: issue: @model.id assignee: assignee - .done => - window.process.finishBackgroundProcess p .fail => @model.set assignee: _assignee, assigneeName: _assigneeName - window.process.failBackgroundProcess p onInputClick: (e) -> @@ -110,13 +106,9 @@ define [ search: (query) -> if query.length > 1 - p = window.process.addBackgroundProcess() $.get "#{baseUrl}/api/users/search", s: query .done (data) => @resetAssignees data.users - window.process.finishBackgroundProcess p - .fail => - window.process.failBackgroundProcess p else @resetAssignees [] diff --git a/server/sonar-web/src/main/coffee/issue/views/comment-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/comment-form-view.coffee index f6f1c831109..d774baf4ac2 100644 --- a/server/sonar-web/src/main/coffee/issue/views/comment-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/comment-form-view.coffee @@ -50,7 +50,6 @@ define [ submit: -> - p = window.process.addBackgroundProcess() text = @ui.textarea.val() update = @model && @model.has('key') method = if update then 'edit_comment' else 'add_comment' @@ -62,7 +61,4 @@ define [ data.issue = @options.issue.id $.post url, data .done => - window.process.finishBackgroundProcess p @options.detailView.updateAfterAction true - .fail => - window.process.failBackgroundProcess p diff --git a/server/sonar-web/src/main/coffee/issue/views/plan-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/plan-form-view.coffee index 837ec7bd7ac..c048dd3c6cf 100644 --- a/server/sonar-web/src/main/coffee/issue/views/plan-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/plan-form-view.coffee @@ -35,7 +35,6 @@ define [ _actionPlan = @getActionPlan() _actionPlanName = @getActionPlanName() return if actionPlan == _actionPlan - p = window.process.addBackgroundProcess() if actionPlan == '' @model.set actionPlan: null, actionPlanName: null else @@ -46,11 +45,8 @@ define [ data: issue: @model.id plan: actionPlan - .done => - window.process.finishBackgroundProcess p .fail => @model.set assignee: _actionPlan, assigneeName: _actionPlanName - window.process.failBackgroundProcess p getActionPlans: -> diff --git a/server/sonar-web/src/main/coffee/issue/views/set-severity-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/set-severity-form-view.coffee index 836ef3babaa..f5e1ff19d94 100644 --- a/server/sonar-web/src/main/coffee/issue/views/set-severity-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/set-severity-form-view.coffee @@ -29,7 +29,6 @@ define [ submit: (severity) -> _severity = @getTransition() return if severity == _severity - p = window.process.addBackgroundProcess() @model.set severity: severity $.ajax type: 'POST' @@ -37,11 +36,8 @@ define [ data: issue: @model.id severity: severity - .done => - window.process.finishBackgroundProcess p .fail => @model.set severity: _severity - window.process.failBackgroundProcess p serializeData: -> diff --git a/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee index 6b32590a270..9ab57bd59b2 100644 --- a/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee @@ -95,18 +95,14 @@ define [ submit: (tags) -> _tags = @getTags() @model.set tags: tags - p = window.process.addBackgroundProcess() $.ajax type: 'POST' url: "#{baseUrl}/api/issues/set_tags" data: key: @model.id tags: tags.join() - .done => - window.process.finishBackgroundProcess p .fail => @model.set tags: _tags - window.process.failBackgroundProcess p onInputClick: (e) -> diff --git a/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee index f389cbf3c6d..6111c4884ad 100644 --- a/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee @@ -23,7 +23,6 @@ define [ submit: (transition) -> - p = window.process.addBackgroundProcess() $.ajax type: 'POST', url: baseUrl + '/api/issues/do_transition', @@ -31,6 +30,4 @@ define [ issue: @model.get('key') transition: transition .done => - @options.view.resetIssue {}, p - .fail => - window.process.failBackgroundProcess p + @options.view.resetIssue {} diff --git a/server/sonar-web/src/main/coffee/issues/app-context.coffee b/server/sonar-web/src/main/coffee/issues/app-context.coffee index c1f688369b7..f12a2fa6d39 100644 --- a/server/sonar-web/src/main/coffee/issues/app-context.coffee +++ b/server/sonar-web/src/main/coffee/issues/app-context.coffee @@ -40,7 +40,6 @@ requirejs [ $ = jQuery App = new Marionette.Application - issuesAppProcess = window.process.addBackgroundProcess() App.getContextQuery = -> @@ -124,7 +123,6 @@ requirejs [ key.setScope 'list' @router = new Router app: @ Backbone.history.start() - window.process.finishBackgroundProcess issuesAppProcess l10nXHR = window.requestMessages() diff --git a/server/sonar-web/src/main/coffee/issues/app-new.coffee b/server/sonar-web/src/main/coffee/issues/app-new.coffee index 8c39a53651b..cff9066f46a 100644 --- a/server/sonar-web/src/main/coffee/issues/app-new.coffee +++ b/server/sonar-web/src/main/coffee/issues/app-new.coffee @@ -40,7 +40,6 @@ requirejs [ $ = jQuery App = new Marionette.Application - issuesAppProcess = window.process.addBackgroundProcess() App.addInitializer -> @state = new State() @@ -103,7 +102,6 @@ requirejs [ key.setScope 'list' @router = new Router app: @ Backbone.history.start() - window.process.finishBackgroundProcess issuesAppProcess l10nXHR = window.requestMessages() diff --git a/server/sonar-web/src/main/coffee/issues/controller.coffee b/server/sonar-web/src/main/coffee/issues/controller.coffee index 5a2f1f313e8..0100182024a 100644 --- a/server/sonar-web/src/main/coffee/issues/controller.coffee +++ b/server/sonar-web/src/main/coffee/issues/controller.coffee @@ -33,7 +33,6 @@ define [ _.extend data, @options.app.state.get 'query' _.extend data, @options.app.state.get 'contextQuery' if @options.app.state.get 'isContext' - fetchIssuesProcess = window.process.addBackgroundProcess() $.get "#{baseUrl}/api/issues/search", data .done (r) => issues = @options.app.list.parseIssues r @@ -51,11 +50,8 @@ define [ pageSize: r.ps total: r.total maxResultsReached: r.p * r.ps >= r.total - window.process.finishBackgroundProcess fetchIssuesProcess if firstPage && @isIssuePermalink() @showComponentViewer @options.app.list.first() - .fail -> - window.process.failBackgroundProcess fetchIssuesProcess isIssuePermalink: -> diff --git a/server/sonar-web/src/main/coffee/issues/workspace-list-item-view.coffee b/server/sonar-web/src/main/coffee/issues/workspace-list-item-view.coffee index 914cb29317f..25435c21f3b 100644 --- a/server/sonar-web/src/main/coffee/issues/workspace-list-item-view.coffee +++ b/server/sonar-web/src/main/coffee/issues/workspace-list-item-view.coffee @@ -33,7 +33,7 @@ define [ @options.app.state.set selectedIndex: @model.get('index') - resetIssue: (options, p) -> + resetIssue: (options) -> key = @model.get 'key' componentUuid = @model.get 'componentUuid' index = @model.get 'index' @@ -42,9 +42,6 @@ define [ @model.fetch(options) .done => @trigger 'reset' - window.process.finishBackgroundProcess p if p? - .fail -> - window.process.failBackgroundProcess p if p? openComponentViewer: -> diff --git a/server/sonar-web/src/main/coffee/quality-gate/app.coffee b/server/sonar-web/src/main/coffee/quality-gate/app.coffee index 076f0dc81ff..28cf9daf416 100644 --- a/server/sonar-web/src/main/coffee/quality-gate/app.coffee +++ b/server/sonar-web/src/main/coffee/quality-gate/app.coffee @@ -18,21 +18,6 @@ requirejs [ QualityGateLayout ) -> - # Create a generic error handler for ajax requests - jQuery.ajaxSetup - error: (jqXHR) -> - text = jqXHR.responseText - errorBox = jQuery('.modal-error') - if jqXHR.responseJSON?.errors? - text = _.pluck(jqXHR.responseJSON.errors, 'msg').join '. ' - else - text = t 'default_error_message' - if errorBox.length > 0 - errorBox.show().text text - else - alert text - - # Add html class to mark the page as navigator page jQuery('html').addClass('navigator-page quality-gates-page'); diff --git a/server/sonar-web/src/main/js/coding-rules/app.js b/server/sonar-web/src/main/js/coding-rules/app.js index 1bdefde9eba..522df2e899a 100644 --- a/server/sonar-web/src/main/js/coding-rules/app.js +++ b/server/sonar-web/src/main/js/coding-rules/app.js @@ -30,8 +30,7 @@ requirejs([ FiltersView) { var $ = jQuery, - App = new Marionette.Application(), - p = window.process.addBackgroundProcess(); + App = new Marionette.Application(); App.addInitializer(function () { this.layout = new Layout(); @@ -83,7 +82,6 @@ requirejs([ app: this }); Backbone.history.start(); - window.process.finishBackgroundProcess(p); }); App.manualRepository = function () { diff --git a/server/sonar-web/src/main/js/coding-rules/bulk-change-modal-view.js b/server/sonar-web/src/main/js/coding-rules/bulk-change-modal-view.js index c1b0a8c3214..b75170d3bb6 100644 --- a/server/sonar-web/src/main/js/coding-rules/bulk-change-modal-view.js +++ b/server/sonar-web/src/main/js/coding-rules/bulk-change-modal-view.js @@ -47,7 +47,6 @@ define([ sendRequests: function (url, options, profiles) { var that = this, - p = window.process.addBackgroundProcess(), looper = $.Deferred().resolve(); profiles.forEach(function (profile) { var opts = _.extend({}, options, { profile_key: profile }); @@ -64,9 +63,6 @@ define([ looper.done(function () { that.options.app.controller.fetchList(); that.$(that.ui.codingRulesSubmitBulkChange.selector).hide(); - window.process.finishBackgroundProcess(p); - }).fail(function () { - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/controller.js b/server/sonar-web/src/main/js/coding-rules/controller.js index 038140b4e06..6e0ec4bccda 100644 --- a/server/sonar-web/src/main/js/coding-rules/controller.js +++ b/server/sonar-web/src/main/js/coding-rules/controller.js @@ -40,8 +40,7 @@ define([ var that = this, url = baseUrl + '/api/rules/search', - options = _.extend(this._searchParameters(), this.app.state.get('query')), - p = window.process.addBackgroundProcess(); + options = _.extend(this._searchParameters(), this.app.state.get('query')); return $.get(url, options).done(function (r) { var rules = that.app.list.parseRules(r); if (firstPage) { @@ -60,12 +59,9 @@ define([ total: r.total, maxResultsReached: r.p * r.ps >= r.total }); - window.process.finishBackgroundProcess(p); if (firstPage && that.isRulePermalink()) { that.showDetails(that.app.list.first()); } - }).fail(function () { - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/rule-details-view.js b/server/sonar-web/src/main/js/coding-rules/rule-details-view.js index c6c6d75cb64..91cf7f8044f 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule-details-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule-details-view.js @@ -139,14 +139,10 @@ define([ title: t('delete'), html: tp('coding_rules.delete.' + ruleType + '.confirm', this.model.get('name')), yesHandler: function () { - var p = window.process.addBackgroundProcess(), - url = baseUrl + '/api/rules/delete', + var url = baseUrl + '/api/rules/delete', options = { key: that.model.id }; $.post(url, options).done(function () { that.options.app.controller.fetchList(); - window.process.finishBackgroundProcess(p); - }).fail(function () { - window.process.failBackgroundProcess(p); }); } }); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-view.js b/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-view.js index 346823ad9af..229c3bb2b88 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-view.js @@ -22,15 +22,11 @@ define([ title: t('delete'), html: t('are_you_sure'), yesHandler: function () { - var p = window.process.addBackgroundProcess(), - url = baseUrl + '/api/rules/delete', + var url = baseUrl + '/api/rules/delete', options = { key: that.model.id }; $.post(url, options).done(function () { that.model.collection.remove(that.model); that.close(); - window.process.finishBackgroundProcess(p); - }).fail(function () { - window.process.failBackgroundProcess(p); }); } }); diff --git a/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js index 0bd198dafac..68cce439208 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/manual-rule-creation-view.js @@ -85,7 +85,6 @@ define([ this.$('.modal-error').hide(); this.$('.modal-warning').hide(); var that = this, - p = window.process.addBackgroundProcess(), url = baseUrl + '/api/rules/' + action; return $.post(url, options).done(function (r) { if (typeof r === 'string') { @@ -100,8 +99,6 @@ define([ } else { that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); } - }).always(function () { - window.process.finishBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js index 1edcf56028e..1de7444ac85 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js @@ -49,7 +49,6 @@ define([ activate: function (e) { e.preventDefault(); var that = this, - p = window.process.addBackgroundProcess(), profileKey = this.ui.qualityProfileSelect.val(), params = this.ui.qualityProfileParameters.map(function () { return { @@ -84,10 +83,8 @@ define([ } }).done(function () { that.trigger('profileActivated', severity, params); - window.process.finishBackgroundProcess(p); }).fail(function () { that.trigger('profileActivationFailed'); - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js index 979f43e1afc..18d4d699855 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-description-view.js @@ -38,8 +38,7 @@ define([ }, submitExtendDescription: function () { - var that = this, - p = window.process.addBackgroundProcess(); + var that = this; this.ui.extendDescriptionForm.addClass('hidden'); return jQuery.ajax({ type: 'POST', @@ -55,10 +54,8 @@ define([ mdNote: r.rule.mdNote }); that.render(); - window.process.finishBackgroundProcess(p); }).fail(function () { that.render(); - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js index bafcdbf2705..ba3b2cdfc43 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js @@ -65,7 +65,6 @@ define([ editDone: function () { var that = this, - p = window.process.addBackgroundProcess(), tags = this.ui.tagInput.val(); return jQuery.ajax({ type: 'POST', @@ -77,10 +76,8 @@ define([ }).done(function (r) { that.model.set('tags', r.rule.tags); that.cancelEdit(); - window.process.finishBackgroundProcess(p); }).always(function () { that.cancelEdit(); - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js b/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js index 4ce97d6a927..68077174dbc 100644 --- a/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js +++ b/server/sonar-web/src/main/js/coding-rules/rule/rule-profile-view.js @@ -50,7 +50,6 @@ define([ title: t('coding_rules.revert_to_parent_definition'), html: tp('coding_rules.revert_to_parent_definition.confirm', this.getParent().name), yesHandler: function () { - var p = window.process.addBackgroundProcess(); return jQuery.ajax({ type: 'POST', url: baseUrl + '/api/qualityprofiles/activate_rule', @@ -60,7 +59,6 @@ define([ reset: true } }).done(function () { - window.process.finishBackgroundProcess(p); that.options.app.controller.showDetails(that.options.rule); }); } @@ -74,7 +72,6 @@ define([ title: t('coding_rules.deactivate'), html: tp('coding_rules.deactivate.confirm'), yesHandler: function () { - var p = window.process.addBackgroundProcess(); return jQuery.ajax({ type: 'POST', url: baseUrl + '/api/qualityprofiles/deactivate_rule', @@ -83,7 +80,6 @@ define([ rule_key: ruleKey } }).done(function () { - window.process.finishBackgroundProcess(p); that.options.app.controller.showDetails(that.options.rule); }); } diff --git a/server/sonar-web/src/main/js/coding-rules/show-app.js b/server/sonar-web/src/main/js/coding-rules/show-app.js index 1505c23ec51..d6df53514c5 100644 --- a/server/sonar-web/src/main/js/coding-rules/show-app.js +++ b/server/sonar-web/src/main/js/coding-rules/show-app.js @@ -11,8 +11,7 @@ requirejs([ RuleDetailsView) { var $ = jQuery, - App = new Marionette.Application(), - p = window.process.addBackgroundProcess(); + App = new Marionette.Application(); App.addInitializer(function () { var url = baseUrl + '/api/rules/show', @@ -28,9 +27,6 @@ requirejs([ actives: data.actives }); this.ruleDetailsView.render().$el.appendTo($('.page')); - window.process.finishBackgroundProcess(p); - }).fail(function () { - window.process.failBackgroundProcess(p); }); }); diff --git a/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js b/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js index 19810a6e5a5..56937b8add9 100644 --- a/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js +++ b/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js @@ -58,7 +58,6 @@ define([ title: t('coding_rules.deactivate'), html: tp('coding_rules.deactivate.confirm'), yesHandler: function () { - var p = window.process.addBackgroundProcess(); return jQuery.ajax({ type: 'POST', url: baseUrl + '/api/qualityprofiles/deactivate_rule', @@ -67,7 +66,6 @@ define([ rule_key: ruleKey } }).done(function () { - window.process.finishBackgroundProcess(p); that.model.unset('activation'); }); } diff --git a/server/sonar-web/src/main/js/common/processes.js b/server/sonar-web/src/main/js/common/processes.js new file mode 100644 index 00000000000..d7188df15ce --- /dev/null +++ b/server/sonar-web/src/main/js/common/processes.js @@ -0,0 +1,163 @@ +(function ($) { + + var options = { + queue: {}, + timeout: 300, + fadeTimeout: 100 + }; + + var Process = Backbone.Model.extend({ + defaults: { + state: 'ok' + }, + + timeout: function () { + this.set({ + state: 'timeout', + message: 'Still Working...' + }); + }, + + finish: function (options) { + options = _.defaults(options || {}, { force: false }); + if (this.get('state') !== 'failed' || !!options.force) { + this.trigger('destroy', this, this.collection, options); + } + }, + + fail: function (message) { + clearInterval(this.get('timer')); + this.set({ + state: 'failed', + message: message || t('process.fail') + }); + this.set('state', 'failed'); + } + }), + + Processes = Backbone.Collection.extend({ + model: Process + }), + + ProcessView = Marionette.ItemView.extend({ + tagName: 'li', + className: 'process-spinner', + + modelEvents: { + 'change': 'render' + }, + + render: function () { + var that = this; + switch (this.model.get('state')) { + case 'timeout': + this.$el.html(this.model.get('message')).addClass('shown'); + break; + case 'failed': + this.$el.html(this.model.get('message')).addClass('process-spinner-failed shown'); + var close = $('').html('').addClass('process-spinner-close'); + close.appendTo(this.$el); + close.on('click', function () { + var a = { force: true }; + that.model.finish(a); + }); + break; + case 'finished': + this.$el.addClass('hidden'); + break; + } + return this; + } + }), + + ProcessesView = Marionette.CollectionView.extend({ + tagName: 'ul', + className: 'processes-container', + itemView: ProcessView + }); + + + var processes = new Processes(), + processesView = new ProcessesView({ + collection: processes + }); + + /** + * Add background process + * @returns {number} + */ + function addBackgroundProcess () { + var uid = _.uniqueId('process'), + process = new Process({ + id: uid, + timer: setTimeout(function () { + process.timeout(); + }, options.timeout) + }); + processes.add(process); + return uid; + } + + /** + * Finish background process + * @param {number} uid + */ + function finishBackgroundProcess (uid) { + var process = processes.get(uid); + if (process != null) { + process.finish(); + } + } + + /** + * Fail background process + * @param {number} uid + * @param {string} message + */ + function failBackgroundProcess (uid, message) { + var process = processes.get(uid); + if (process != null) { + process.fail(message); + } + } + + /** + * Handle ajax error + * @param jqXHR + */ + function handleAjaxError (jqXHR) { + if (jqXHR.processId != null) { + var message = null; + if (jqXHR != null && jqXHR.responseJSON != null && jqXHR.responseJSON.errors != null) { + message = _.pluck(jqXHR.responseJSON.errors, 'msg').join('. '); + } + failBackgroundProcess(jqXHR.processId, message); + } + } + + + $.ajaxSetup({ + beforeSend: function (jqXHR) { + jqXHR.processId = addBackgroundProcess(); + }, + complete: function (jqXHR) { + if (jqXHR.processId != null) { + finishBackgroundProcess(jqXHR.processId); + } + }, + statusCode: { + 400: handleAjaxError, + 401: handleAjaxError, + 403: handleAjaxError, + 500: handleAjaxError + } + }); + + + $(function () { + + processesView.render().$el.appendTo(document.body); + + }); + +})(window.jQuery); diff --git a/server/sonar-web/src/main/js/components/navigator/controller.js b/server/sonar-web/src/main/js/components/navigator/controller.js index ccbee03e657..b064a6d9570 100644 --- a/server/sonar-web/src/main/js/components/navigator/controller.js +++ b/server/sonar-web/src/main/js/components/navigator/controller.js @@ -50,14 +50,9 @@ define(function () { if (facet.has('values') || this.options.app.state.get('facetsFromServer').indexOf(id) === -1) { facet.set({enabled: true}); } else { - var p = window.process.addBackgroundProcess(); this.requestFacet(id) .done(function () { facet.set({enabled: true}); - window.process.finishBackgroundProcess(p); - }) - .fail(function () { - window.process.failBackgroundProcess(p); }); } }, diff --git a/server/sonar-web/src/main/js/nav/context-navbar-view.js b/server/sonar-web/src/main/js/nav/context-navbar-view.js index 8a7935c8919..f045a2f770a 100644 --- a/server/sonar-web/src/main/js/nav/context-navbar-view.js +++ b/server/sonar-web/src/main/js/nav/context-navbar-view.js @@ -23,15 +23,11 @@ define([ onFavoriteClick: function () { var that = this, - p = window.process.addBackgroundProcess(), url = baseUrl + '/favourites/toggle/' + this.model.get('contextId'), isContextFavorite = this.model.get('isContextFavorite'); this.model.set({ isContextFavorite: !isContextFavorite }); - return $.post(url).done(function () { - window.process.finishBackgroundProcess(p); - }).fail(function () { + return $.post(url).fail(function () { that.model.set({ isContextFavorite: isContextFavorite }); - window.process.failBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/nav/search-view.js b/server/sonar-web/src/main/js/nav/search-view.js index e3258e61477..243fef610f4 100644 --- a/server/sonar-web/src/main/js/nav/search-view.js +++ b/server/sonar-web/src/main/js/nav/search-view.js @@ -130,8 +130,7 @@ define([ } var that = this, url = baseUrl + '/api/components/suggestions', - options = { s: q }, - p = window.process.addBackgroundProcess(); + options = { s: q }; return $.get(url, options).done(function (r) { var collection = []; r.results.forEach(function (domain) { @@ -144,9 +143,6 @@ define([ }); }); that.results.reset(collection); - window.process.finishBackgroundProcess(p); - }).fail(function() { - window.process.failBackgroundProcess(p); }); } }); diff --git a/server/sonar-web/src/main/js/source-viewer/measures-overlay.js b/server/sonar-web/src/main/js/source-viewer/measures-overlay.js index 3a1cc491d9a..6418f6e0e96 100644 --- a/server/sonar-web/src/main/js/source-viewer/measures-overlay.js +++ b/server/sonar-web/src/main/js/source-viewer/measures-overlay.js @@ -12,7 +12,6 @@ define([ initialize: function () { var that = this, - p = window.process.addBackgroundProcess(), requests = [this.requestMeasures(), this.requestIssues()]; if (this.model.get('isUnitTest')) { requests.push(this.requestTests()); @@ -20,9 +19,6 @@ define([ this.testsScroll = 0; $.when.apply($, requests).done(function () { that.render(); - window.process.finishBackgroundProcess(p); - }).fail(function () { - window.process.failBackgroundProcess(p); }); }, @@ -203,7 +199,6 @@ define([ showTest: function (e) { var that = this, - p = window.process.addBackgroundProcess(), name = $(e.currentTarget).data('name'), url = baseUrl + '/api/tests/covered_files', options = { @@ -215,7 +210,6 @@ define([ that.coveredFiles = data.files; that.selectedTest = _.findWhere(that.model.get('tests'), { name: name }); that.render(); - window.process.finishBackgroundProcess(p); }); }, diff --git a/server/sonar-web/src/main/js/source-viewer/viewer.js b/server/sonar-web/src/main/js/source-viewer/viewer.js index a4a6575c632..a8ee471090d 100644 --- a/server/sonar-web/src/main/js/source-viewer/viewer.js +++ b/server/sonar-web/src/main/js/source-viewer/viewer.js @@ -97,11 +97,9 @@ define([ open: function (id) { var that = this, - r = window.process.addBackgroundProcess(), finalize = function () { that.requestIssues().done(function () { that.render(); - window.process.finishBackgroundProcess(r); that.trigger('loaded'); }); }; @@ -343,8 +341,7 @@ define([ showCoveragePopup: function (e) { e.stopPropagation(); $('body').click(); - var r = window.process.addBackgroundProcess(), - line = $(e.currentTarget).data('line-number'), + var line = $(e.currentTarget).data('line-number'), row = _.findWhere(this.model.get('source'), {line: line}), url = baseUrl + '/api/tests/test_cases', options = { @@ -358,9 +355,6 @@ define([ triggerEl: $(e.currentTarget) }); popup.render(); - window.process.finishBackgroundProcess(r); - }).fail(function () { - window.process.failBackgroundProcess(r); }); }, diff --git a/server/sonar-web/src/main/less/ui.less b/server/sonar-web/src/main/less/ui.less index f51a4b7c319..eec3b654e70 100644 --- a/server/sonar-web/src/main/less/ui.less +++ b/server/sonar-web/src/main/less/ui.less @@ -352,18 +352,21 @@ input[type=button] { } +.processes-container { + position: fixed; + z-index: 9999; + top: 0; + left: 50%; + width: 350px; + margin-left: -175px; +} .process-spinner { - position: fixed; - z-index: 2000; - top: 0; left: 50%; - min-width: 300px; - max-width: 900px; - margin-left: -150px; padding: 0 10px; line-height: @formControlHeight; - background-color: #f0e8ac; border-radius: 0 0 3px 3px; + .box-sizing(border-box); + background-color: #f0e8ac; text-align: center; opacity: 0; .trans; @@ -371,14 +374,21 @@ input[type=button] { &.shown { opacity: 1; } } +.process-spinner + .process-spinner { + margin-top: 5px; + border-radius: 3px; +} + .process-spinner-failed { + padding-right: 30px; background-color: @red; color: @white; } .process-spinner-close { - float: right; - margin-right: 5px; + position: absolute; + top: 0; + right: 0; padding: 3px; background: none !important; border: none !important; -- 2.39.5