diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2015-03-09 10:46:14 +0100 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2015-03-09 10:46:14 +0100 |
commit | 8260aba269d3d8815d3c0e2716732b8db6db1880 (patch) | |
tree | 7d16078bc692f738c861f4849fd94b7861e1966a /server | |
parent | 1c2a7ac3b8da5dbe4efaace5893db1f595ae173e (diff) | |
download | sonarqube-8260aba269d3d8815d3c0e2716732b8db6db1880.tar.gz sonarqube-8260aba269d3d8815d3c0e2716732b8db6db1880.zip |
rewrite dsm app
Diffstat (limited to 'server')
-rw-r--r-- | server/sonar-web/src/main/coffee/design/app.coffee | 77 | ||||
-rw-r--r-- | server/sonar-web/src/main/coffee/design/info-view.coffee | 33 | ||||
-rw-r--r-- | server/sonar-web/src/main/coffee/design/view.coffee | 155 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/design/app.js | 88 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/design/info-view.js | 33 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/design/view.js | 177 | ||||
-rw-r--r-- | server/sonar-web/src/test/js/design-spec.js | 54 | ||||
-rw-r--r-- | server/sonar-web/src/test/views/design.jade | 1 |
8 files changed, 350 insertions, 268 deletions
diff --git a/server/sonar-web/src/main/coffee/design/app.coffee b/server/sonar-web/src/main/coffee/design/app.coffee deleted file mode 100644 index 1a54425acd4..00000000000 --- a/server/sonar-web/src/main/coffee/design/app.coffee +++ /dev/null @@ -1,77 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2014 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3 of the License, or (at your option) any later version. -# -# SonarQube is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -requirejs.config - baseUrl: "#{baseUrl}/js" - - -requirejs [ - 'design/view' -], ( - DesignView -) -> - - $ = jQuery - RESOURCES_URL = "#{baseUrl}/api/resources" - App = new Marionette.Application - - - App.noDataAvailable = -> - message = t 'design.noData' - $('#project-design').html "<p class=\"message-alert\"><i class=\"icon-alert-warn\"></i> #{message}</p>" - - - App.addInitializer -> - packageTangles = {} - - packageTanglesXHR = $.get RESOURCES_URL, resource: window.resourceKey, depth: 1, metrics: 'package_tangles', (data) -> - data.forEach (component) -> - packageTangles[component.id] = component.msr[0].frmt_val - - dsmXHR = $.get RESOURCES_URL, resource: window.resourceKey, metrics: 'dsm' - dsmXHR.fail -> App.noDataAvailable() - - $.when(packageTanglesXHR, dsmXHR).done -> - rawData = dsmXHR.responseJSON - unless _.isArray(rawData) && rawData.length == 1 && _.isArray(rawData[0].msr) - App.noDataAvailable() - return - data = JSON.parse rawData[0].msr[0].data - data.forEach (row, rowIndex) -> - row.v.forEach (cell, columnIndex) -> - if cell.w? && cell.w > 0 - if rowIndex < columnIndex - cell.status = 'cycle' - else - cell.status = 'dependency' - data = data.map (row) -> - _.extend row, empty: row.q == 'DIR' && row.v.every (item) -> !item.w? - collection = new Backbone.Collection data - collection.forEach (model) -> - model.set 'pt', packageTangles[model.get 'i'] - @view = new DesignView app: @, collection: collection - $('#project-design').empty().append @view.render().el - - - # Message bundles - l10nXHR = window.requestMessages() - - - jQuery.when(l10nXHR).done -> App.start() diff --git a/server/sonar-web/src/main/coffee/design/info-view.coffee b/server/sonar-web/src/main/coffee/design/info-view.coffee deleted file mode 100644 index b626ce86099..00000000000 --- a/server/sonar-web/src/main/coffee/design/info-view.coffee +++ /dev/null @@ -1,33 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2014 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3 of the License, or (at your option) any later version. -# -# SonarQube is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -define [ - 'templates/design' -], -> - - - class extends Marionette.ItemView - template: Templates['dsm-info'] - - - serializeData: -> - _.extend super, - first: @options.first - second: @options.second diff --git a/server/sonar-web/src/main/coffee/design/view.coffee b/server/sonar-web/src/main/coffee/design/view.coffee deleted file mode 100644 index 44c57a6976a..00000000000 --- a/server/sonar-web/src/main/coffee/design/view.coffee +++ /dev/null @@ -1,155 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2014 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3 of the License, or (at your option) any later version. -# -# SonarQube is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -define [ - 'design/info-view', - 'templates/design' -], (InfoView) -> - $ = jQuery - API_DEPENDECIES = "#{baseUrl}/api/dependencies" - - - class extends Marionette.Layout - template: Templates['design'] - className: 'dsm' - - - regions: - infoRegion: '.dsm-info' - - - ui: - titles: '.dsm-body-title' - cells: '.dsm-body-cell' - dependencies: '.dsm-body-dependency' - - - events: - 'click @ui.titles': 'highlightComponent' - 'dblclick @ui.titles': 'goToComponent' - 'click @ui.cells': 'highlightCell' - 'dblclick @ui.dependencies': 'showDependencies' - 'change .js-hide-dir': 'toggleDirDisplay' - - - onRender: -> - @toggleDirDisplay() - - - clearCells: -> - @ui.titles.removeClass 'dsm-body-highlighted dsm-body-usage dsm-body-dependency' - @ui.cells.removeClass 'dsm-body-highlighted dsm-body-usage dsm-body-dependency' - - - highlightComponent: (e) -> - index = @ui.titles.index $(e.currentTarget) - @clearCells() - @highlightRow index - @highlightColumn index - @highlightUsages index - @highlightDependencies index - - - highlightCell: (e) -> - cell = $(e.currentTarget) - column = cell.parent().children().index(cell) - 1 - row = cell.parent().parent().children().index cell.parent() - @clearCells() - if row == column - @highlightRow row - @highlightColumn row - @highlightUsages row - @highlightDependencies row - else - @highlightRow column, 'dsm-body-usage' - @highlightColumn column, 'dsm-body-usage' - @highlightRow row, 'dsm-body-dependency' - @highlightColumn row, 'dsm-body-dependency' - - - highlightRow: (index, c = 'dsm-body-highlighted') -> - @$(".dsm-body tr:eq(#{index}) td").addClass c - - - highlightColumn: (index, c = 'dsm-body-highlighted') -> - @$(".dsm-body tr").each -> - $(this).find("td:eq(#{index + 1})").addClass c - - - highlightUsages: (index) -> - @collection.at(index).get('v').forEach (d, i) => - if i < index && d.w? - @$("tr:eq(#{i}) .dsm-body-title").addClass 'dsm-body-usage' - - - highlightDependencies: (index) -> - @collection.forEach (model, i) => - if model.get('v')[index].w? - @$("tr:eq(#{i}) .dsm-body-title").addClass 'dsm-body-dependency' - - - goToComponent: (e) -> - cell = $(e.currentTarget) - row = cell.parent().parent().children().index cell.parent() - model = @collection.at(row) - page = if model.get('q') == 'CLA' || model.get('q') == 'FIL' then 'dashboard' else 'design' - window.location = "#{baseUrl}/#{page}/index/#{model.get 'i'}" - - - showDependencies: (e) -> - cell = $(e.currentTarget) - column = cell.parent().children().index(cell) - 1 - row = cell.parent().parent().children().index cell.parent() - id = @collection.at(row).get('v')[column].i - return unless id - @showInfoViewSpinner() - @scrollToInfoView() - $.get API_DEPENDECIES, parent: id, (data) => - @infoRegion.show new InfoView - collection: new Backbone.Collection data - first: @collection.at(column).toJSON() - second: @collection.at(row).toJSON() - @scrollToInfoView() - - - showInfoViewSpinner: -> - @infoRegion.reset() - @$(@infoRegion.el).html '<i class="spinner"></i>' - - - scrollToInfoView: -> - delta = @$(@infoRegion.el).offset().top - 40 - $('html, body').animate { scrollTop: delta }, 500 - - - toggleDirDisplay: -> - rows = @$('tr') - rows.each (index) -> - if $(@).data('empty')? - $(@).toggleClass 'hidden' - rows.each -> - $(@).find('td').eq(index + 1).toggleClass 'hidden' - - - serializeData: -> - hasDirectories = @collection.some (model) -> model.get('q') == 'DIR' - _.extend super, - hasDirectories: hasDirectories - diff --git a/server/sonar-web/src/main/js/design/app.js b/server/sonar-web/src/main/js/design/app.js new file mode 100644 index 00000000000..c9bce071937 --- /dev/null +++ b/server/sonar-web/src/main/js/design/app.js @@ -0,0 +1,88 @@ +/* + * 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(['design/view'], function (DesignView) { + + var $ = jQuery, + RESOURCES_URL = baseUrl + '/api/resources', + App = new Marionette.Application(); + + App.noDataAvailable = function () { + var message = t('design.noData'); + $('#project-design').html('<p class="message-alert"><i class="icon-alert-warn"></i> ' + message + '</p>'); + }; + + App.addInitializer(function () { + var packageTangles = {}, + packageTanglesXHR = $.get(RESOURCES_URL, { + resource: window.resourceKey, + depth: 1, + metrics: 'package_tangles' + }).done(function (data) { + return data.forEach(function (component) { + packageTangles[component.id] = component.msr[0].frmt_val; + }); + }), + dsmXHR = $.get(RESOURCES_URL, { + resource: window.resourceKey, + metrics: 'dsm' + }).fail(function () { + App.noDataAvailable(); + }); + + $.when(packageTanglesXHR, dsmXHR).done(function () { + var rawData = dsmXHR.responseJSON; + if (!(_.isArray(rawData) && rawData.length === 1 && _.isArray(rawData[0].msr))) { + App.noDataAvailable(); + return; + } + + var data = JSON.parse(rawData[0].msr[0].data); + data.forEach(function (row, rowIndex) { + return row.v.forEach(function (cell, columnIndex) { + if ((cell.w != null) && cell.w > 0) { + cell.status = rowIndex < columnIndex ? 'cycle' : 'dependency'; + } + }); + }); + data = data.map(function (row) { + return _.extend(row, { + empty: row.q === 'DIR' && row.v.every(function (item) { + return item.w == null; + }) + }); + }); + + var collection = new Backbone.Collection(data); + collection.forEach(function (model) { + return model.set('pt', packageTangles[model.get('i')]); + }); + this.view = new DesignView({ + app: this, + collection: collection + }); + $('#project-design').empty().append(this.view.render().el); + }); + }); + + window.requestMessages().done(function () { + App.start(); + }); + +}); diff --git a/server/sonar-web/src/main/js/design/info-view.js b/server/sonar-web/src/main/js/design/info-view.js new file mode 100644 index 00000000000..0c39aaf02f8 --- /dev/null +++ b/server/sonar-web/src/main/js/design/info-view.js @@ -0,0 +1,33 @@ +/* + * 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/design'], function () { + + return Marionette.ItemView.extend({ + template: Templates['dsm-info'], + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + first: this.options.first, + second: this.options.second + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/design/view.js b/server/sonar-web/src/main/js/design/view.js new file mode 100644 index 00000000000..66db089f1c3 --- /dev/null +++ b/server/sonar-web/src/main/js/design/view.js @@ -0,0 +1,177 @@ +/* + * 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(['design/info-view', 'templates/design'], function (InfoView) { + var $ = jQuery, + API_DEPENDENCIES = baseUrl + '/api/dependencies'; + + return Marionette.Layout.extend({ + template: Templates['design'], + className: 'dsm', + + regions: { + infoRegion: '.dsm-info' + }, + + ui: { + titles: '.dsm-body-title', + cells: '.dsm-body-cell', + dependencies: '.dsm-body-dependency' + }, + + events: { + 'click @ui.titles': 'highlightComponent', + 'dblclick @ui.titles': 'goToComponent', + 'click @ui.cells': 'highlightCell', + 'dblclick @ui.dependencies': 'showDependencies', + 'change .js-hide-dir': 'toggleDirDisplay' + }, + + onRender: function () { + this.toggleDirDisplay(); + }, + + clearCells: function () { + this.ui.titles.removeClass('dsm-body-highlighted dsm-body-usage dsm-body-dependency'); + this.ui.cells.removeClass('dsm-body-highlighted dsm-body-usage dsm-body-dependency'); + }, + + highlightComponent: function (e) { + var index = this.ui.titles.index($(e.currentTarget)); + this.clearCells(); + this.highlightRow(index); + this.highlightColumn(index); + this.highlightUsages(index); + this.highlightDependencies(index); + }, + + highlightCell: function (e) { + var cell = $(e.currentTarget), + column = cell.parent().children().index(cell) - 1, + row = cell.parent().parent().children().index(cell.parent()); + this.clearCells(); + if (row === column) { + this.highlightRow(row); + this.highlightColumn(row); + this.highlightUsages(row); + this.highlightDependencies(row); + } else { + this.highlightRow(column, 'dsm-body-usage'); + this.highlightColumn(column, 'dsm-body-usage'); + this.highlightRow(row, 'dsm-body-dependency'); + this.highlightColumn(row, 'dsm-body-dependency'); + } + }, + + highlightRow: function (index, c) { + if (c == null) { + c = 'dsm-body-highlighted'; + } + this.$('.dsm-body').find('tr:eq(' + index + ')').find('td').addClass(c); + }, + + highlightColumn: function (index, c) { + if (c == null) { + c = 'dsm-body-highlighted'; + } + this.$('.dsm-body tr').each(function () { + return $(this).find('td:eq(' + (index + 1) + ')').addClass(c); + }); + }, + + highlightUsages: function (index) { + var that = this; + this.collection.at(index).get('v').forEach(function (d, i) { + if (i < index && (d.w != null)) { + that.$('tr:eq(' + i + ')').find('.dsm-body-title').addClass('dsm-body-usage'); + } + }); + }, + + highlightDependencies: function (index) { + var that = this; + this.collection.forEach(function (model, i) { + if (model.get('v')[index].w != null) { + that.$('tr:eq(' + i + ')').find('.dsm-body-title').addClass('dsm-body-dependency'); + } + }); + }, + + goToComponent: function (e) { + var cell = $(e.currentTarget), + row = cell.parent().parent().children().index(cell.parent()), + model = this.collection.at(row), + page = model.get('q') === 'CLA' || model.get('q') === 'FIL' ? 'dashboard' : 'design'; + window.location = baseUrl + '/' + page + '/index/' + model.get('i'); + }, + + showDependencies: function (e) { + var that = this, + cell = $(e.currentTarget), + column = cell.parent().children().index(cell) - 1, + row = cell.parent().parent().children().index(cell.parent()), + id = this.collection.at(row).get('v')[column].i; + if (!id) { + return; + } + this.showInfoViewSpinner(); + this.scrollToInfoView(); + return $.get(API_DEPENDENCIES, { parent: id }).done(function (data) { + that.infoRegion.show(new InfoView({ + collection: new Backbone.Collection(data), + first: that.collection.at(column).toJSON(), + second: that.collection.at(row).toJSON() + })); + that.scrollToInfoView(); + }); + }, + + showInfoViewSpinner: function () { + this.infoRegion.reset(); + this.$(this.infoRegion.el).html('<i class="spinner"></i>'); + }, + + scrollToInfoView: function () { + var delta = this.$(this.infoRegion.el).offset().top - 40; + $('html, body').animate({ scrollTop: delta }, 500); + }, + + toggleDirDisplay: function () { + var rows = this.$('tr'); + rows.each(function (index) { + if ($(this).data('empty') != null) { + $(this).toggleClass('hidden'); + rows.each(function () { + $(this).find('td').eq(index + 1).toggleClass('hidden'); + }); + } + }); + }, + + serializeData: function () { + var hasDirectories = this.collection.some(function (model) { + return model.get('q') === 'DIR'; + }); + return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), { + hasDirectories: hasDirectories + }); + } + }); + +}); diff --git a/server/sonar-web/src/test/js/design-spec.js b/server/sonar-web/src/test/js/design-spec.js index d6989047711..08bb92138e1 100644 --- a/server/sonar-web/src/test/js/design-spec.js +++ b/server/sonar-web/src/test/js/design-spec.js @@ -17,15 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* globals casper: false */ var lib = require('../lib'), - testName = lib.testName('Design'); + testName = lib.testName('DSM'); lib.initMessages(); lib.changeWorkingDirectory('design-spec'); lib.configureCasper(); -casper.test.begin(testName('Base'), function suite(test) { +casper.test.begin(testName('Base'), 9, function (test) { casper .start(lib.buildUrl('design'), function () { lib.setDefaultViewport(); @@ -50,7 +51,7 @@ casper.test.begin(testName('Base'), function suite(test) { casper.mouse.doubleclick('.dsm-body-cell-cycle'); casper.waitForSelector('.spinner', function () { casper.waitWhileSelector('.spinner'); - }) + }); }) .then(function () { @@ -68,3 +69,50 @@ casper.test.begin(testName('Base'), function suite(test) { test.done(); }); }); + +casper.test.begin(testName('Highlight'), 13, function (test) { + casper + .start(lib.buildUrl('design'), function () { + lib.setDefaultViewport(); + lib.mockRequest('/api/l10n/index', '{}'); + lib.mockRequestFromFile('/api/resources', 'resources.json'); + lib.mockRequestFromFile('/api/dependencies', 'dependencies.json'); + }) + + .then(function () { + casper.waitWhileSelector('.spinner'); + }) + + .then(function () { + casper.click('tr:nth-child(2) > .dsm-body-title'); + test.assertElementCount('.dsm-body-highlighted', 12); + test.assertElementCount('tr:nth-child(2) .dsm-body-highlighted', 7); + test.assertElementCount('td:nth-child(3).dsm-body-highlighted', 6); + }) + + .then(function () { + casper.click('tr:nth-child(3) > td:nth-child(5)'); + test.assertElementCount('.dsm-body-dependency', 12); + test.assertElementCount('tr:nth-child(3) .dsm-body-dependency', 7); + test.assertElementCount('td:nth-child(4).dsm-body-dependency', 6); + test.assertElementCount('.dsm-body-usage', 12); + test.assertElementCount('tr:nth-child(4) .dsm-body-usage', 7); + test.assertElementCount('td:nth-child(5).dsm-body-usage', 6); + test.assertElementCount('.dsm-body-dependency.dsm-body-usage', 2); + }) + + .then(function () { + casper.click('tr:nth-child(2) > td:nth-child(3)'); + test.assertElementCount('.dsm-body-highlighted', 12); + test.assertElementCount('tr:nth-child(2) .dsm-body-highlighted', 7); + test.assertElementCount('td:nth-child(3).dsm-body-highlighted', 6); + }) + + .then(function () { + lib.sendCoverage(); + }) + + .run(function () { + test.done(); + }); +}); diff --git a/server/sonar-web/src/test/views/design.jade b/server/sonar-web/src/test/views/design.jade index ab71d7af887..d64927901fe 100644 --- a/server/sonar-web/src/test/views/design.jade +++ b/server/sonar-web/src/test/views/design.jade @@ -3,6 +3,7 @@ extends layouts/main block header script(src='../js/require.js') script. + requirejs.config({ baseUrl: "/js" }); window.waitForMocks('/js/design/app.js'); block body |