diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2014-12-18 15:17:57 +0100 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2014-12-22 10:46:42 +0100 |
commit | 4b69d42d7f83aa22853dffbf2b49acda85c7ef99 (patch) | |
tree | 9a92219c8ebc0afeade341755e7ba92ded6502f3 /server/sonar-web/src/main/js/components/navigator | |
parent | 2b6c121e6764b6baf106c94767d8daedd85b7839 (diff) | |
download | sonarqube-4b69d42d7f83aa22853dffbf2b49acda85c7ef99.tar.gz sonarqube-4b69d42d7f83aa22853dffbf2b49acda85c7ef99.zip |
SONAR-5820 Create a new coding rules page
Diffstat (limited to 'server/sonar-web/src/main/js/components/navigator')
10 files changed, 555 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/components/navigator/controller.js b/server/sonar-web/src/main/js/components/navigator/controller.js new file mode 100644 index 00000000000..d1ae597ce0a --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/controller.js @@ -0,0 +1,142 @@ +define([ + 'backbone.marionette' +], function (Marionette) { + + return Marionette.Controller.extend({ + pageSize: 50, + allFacets: [], + facetsFromServer: [], + transform: {}, + + initialize: function (options) { + this.app = options.app; + this.listenTo(options.app.state, 'change:query', this.fetchList); + }, + + _allFacets: function () { + return this.allFacets.map(function (facet) { + return {property: facet}; + }); + }, + + _enabledFacets: function () { + var that = this, + facets = this.options.app.state.get('facets'), + criteria = Object.keys(this.options.app.state.get('query')); + facets = facets.concat(criteria); + facets = facets.map(function (facet) { + return that.transform[facet] != null ? that.transform[facet] : facet; + }); + return facets.filter(function (facet) { + return that.allFacets.indexOf(facet) !== -1; + }); + }, + + _facetsFromServer: function () { + var that = this, + facets = this._enabledFacets(); + return facets.filter(function (facet) { + return that.facetsFromServer.indexOf(facet) !== -1; + }); + }, + + fetchList: function () { + + }, + + fetchNextPage: function () { + this.options.app.state.nextPage(); + return this.fetchList(false); + }, + + enableFacet: function (id) { + var facet = this.options.app.facets.get(id); + if (facet.has('values') || this.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); + }); + } + }, + + disableFacet: function (id) { + var facet = this.options.app.facets.get(id); + facet.set({enabled: false}); + this.options.app.facetsView.children.findByModel(facet).disable(); + }, + + toggleFacet: function (id) { + var facet = this.options.app.facets.get(id); + if (facet.get('enabled')) { + this.disableFacet(id); + } else { + this.enableFacet(id); + } + }, + + enableFacets: function (facets) { + facets.forEach(this.enableFacet, this); + }, + + newSearch: function () { + this.options.app.state.setQuery({}); + }, + + parseQuery: function (query, separator) { + separator = separator || '|'; + var q = {}; + (query || '').split(separator).forEach(function (t) { + var tokens = t.split('='); + if (tokens[0] && tokens[1] != null) { + q[tokens[0]] = decodeURIComponent(tokens[1]); + } + }); + return q; + }, + + getQuery: function (separator) { + separator = separator || '|'; + var filter = this.options.app.state.get('query'), + route = []; + _.map(filter, function (value, property) { + route.push('' + property + '=' + encodeURIComponent(value)); + }); + return route.join(separator); + }, + + getRoute: function (separator) { + separator = separator || '|'; + return this.getQuery(separator); + }, + + selectNext: function () { + var index = this.options.app.state.get('selectedIndex') + 1; + if (index < this.options.app.list.length) { + this.options.app.state.set({ selectedIndex: index }); + } else { + if (!this.options.app.state.get('maxResultsReached')) { + var that = this; + this.fetchNextPage().done(function () { + that.options.app.state.set({ selectedIndex: index }); + }); + } + } + }, + + selectPrev: function () { + var index = this.options.app.state.get('selectedIndex') - 1; + if (index >= 0) { + this.options.app.state.set({ selectedIndex: index }); + } + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/facets-view.js b/server/sonar-web/src/main/js/components/navigator/facets-view.js new file mode 100644 index 00000000000..0d3f9102833 --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/facets-view.js @@ -0,0 +1,37 @@ +define([ + 'backbone.marionette', + 'components/navigator/facets/base-facet' +], function (Marionette, BaseFacet) { + + return Marionette.CollectionView.extend({ + className: 'search-navigator-facets-list', + + itemViewOptions: function () { + return { + app: this.options.app + }; + }, + + getItemView: function () { + return BaseFacet; + }, + + collectionEvents: function () { + return { + 'change:enabled': 'updateState' + }; + }, + + updateState: function () { + var enabledFacets = this.collection.filter(function (model) { + return model.get('enabled'); + }), + enabledFacetIds = enabledFacets.map(function (model) { + return model.id; + }); + this.options.app.state.set({facets: enabledFacetIds}); + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/facets/base-facet.js b/server/sonar-web/src/main/js/components/navigator/facets/base-facet.js new file mode 100644 index 00000000000..c2c894ab45a --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/facets/base-facet.js @@ -0,0 +1,76 @@ +define([ + 'backbone.marionette' +], function (Marionette) { + + var $ = jQuery; + + return Marionette.ItemView.extend({ + className: 'search-navigator-facet-box', + + modelEvents: function () { + return { + 'change': 'render' + }; + }, + + events: function () { + return { + 'click .js-facet-toggle': 'toggle', + 'click .js-facet': 'toggleFacet' + }; + }, + + onRender: function () { + this.$el.toggleClass('search-navigator-facet-box-collapsed', !this.model.get('enabled')); + var that = this, + property = this.model.get('property'), + value = this.options.app.state.get('query')[property]; + if (typeof value === 'string') { + value.split(',').forEach(function (s) { + var facet = that.$('.js-facet').filter('[data-value="' + s + '"]'); + if (facet.length > 0) { + facet.addClass('active'); + } + }); + } + }, + + toggle: function () { + this.options.app.controller.toggleFacet(this.model.id); + }, + + getValue: function () { + return this.$('.js-facet.active').map(function () { + return $(this).data('value'); + }).get().join(); + }, + + toggleFacet: function (e) { + $(e.currentTarget).toggleClass('active'); + var property = this.model.get('property'), + obj = {}; + obj[property] = this.getValue(); + this.options.app.state.updateFilter(obj); + }, + + disable: function () { + var property = this.model.get('property'), + obj = {}; + obj[property] = null; + this.options.app.state.updateFilter(obj); + }, + + sortValues: function (values) { + return _.sortBy(values, function (v) { + return -v.count; + }); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + values: this.sortValues(this.model.getValues()) + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/models/facet.js b/server/sonar-web/src/main/js/components/navigator/models/facet.js new file mode 100644 index 00000000000..7d15da0507a --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/models/facet.js @@ -0,0 +1,22 @@ +define([ + 'backbone' +], function (Backbone) { + + return Backbone.Model.extend({ + idAttribute: 'property', + + defaults: { + enabled: false + }, + + getValues: function () { + return this.get('values') || []; + }, + + toggle: function () { + var enabled = this.get('enabled'); + this.set({ enabled: !enabled }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/models/facets.js b/server/sonar-web/src/main/js/components/navigator/models/facets.js new file mode 100644 index 00000000000..88929deb8da --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/models/facets.js @@ -0,0 +1,10 @@ +define([ + 'backbone', + 'components/navigator/models/facet' +], function (Backbone, Facet) { + + return Backbone.Collection.extend({ + model: Facet + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/models/state.js b/server/sonar-web/src/main/js/components/navigator/models/state.js new file mode 100644 index 00000000000..8a4f2ff669c --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/models/state.js @@ -0,0 +1,52 @@ +define([ + 'backbone' +], function (Backbone) { + + return Backbone.Model.extend({ + defaults: { + page: 1, + maxResultsReached: false, + query: {}, + facets: [] + }, + + nextPage: function () { + var page = this.get('page'); + this.set({ page: page + 1 }); + }, + + clearQuery: function (query) { + var q = {}; + Object.keys(query).forEach(function (key) { + if (query[key]) { + q[key] = query[key]; + } + }); + return q; + }, + + _areQueriesEqual: function (a, b) { + var equal = Object.keys(a).length === Object.keys(b).length; + Object.keys(a).forEach(function (key) { + equal = equal && a[key] === b[key]; + }); + return equal; + }, + + updateFilter: function (obj) { + var oldQuery = this.get('query'), + query = _.extend({}, oldQuery, obj); + query = this.clearQuery(query); + if (!this._areQueriesEqual(oldQuery, query)) { + this.setQuery(query); + } + }, + + setQuery: function (query) { + this.set({ query: query }, { silent: true }); + this.set({ changed: true }); + this.trigger('change:query'); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/router.js b/server/sonar-web/src/main/js/components/navigator/router.js new file mode 100644 index 00000000000..8792dd519c7 --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/router.js @@ -0,0 +1,29 @@ +define([ + 'backbone' +], function (Backbone) { + + return Backbone.Router.extend({ + routeSeparator: '|', + + routes: { + '': 'index', + ':query': 'index' + }, + + initialize: function (options) { + this.options = options; + this.listenTo(this.options.app.state, 'change:query', this.updateRoute); + }, + + index: function (query) { + query = this.options.app.controller.parseQuery(query); + this.options.app.state.setQuery(query); + }, + + updateRoute: function () { + var route = this.options.app.controller.getRoute(); + this.navigate(route); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js b/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js new file mode 100644 index 00000000000..aec6efa6284 --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/workspace-header-view.js @@ -0,0 +1,49 @@ +define([ + 'backbone.marionette' +], function (Marionette) { + + return Marionette.ItemView.extend({ + + collectionEvents: function () { + return { + 'all': 'render' + }; + }, + + events: function () { + return { + 'click .js-bulk-change': 'bulkChange', + 'click .js-reload': 'reload', + 'click .js-next': 'selectNext', + 'click .js-prev': 'selectPrev' + }; + }, + + initialize: function (options) { + this.listenTo(options.app.state, 'change', this.render); + }, + + bulkChange: function () { + + }, + + reload: function () { + this.options.app.controller.fetchList(); + }, + + selectNext: function () { + this.options.app.controller.selectNext(); + }, + + selectPrev: function () { + this.options.app.controller.selectPrev(); + }, + + serializeData: function () { + return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { + state: this.options.app.state.toJSON() + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/workspace-list-item-view.js b/server/sonar-web/src/main/js/components/navigator/workspace-list-item-view.js new file mode 100644 index 00000000000..b4bb669303a --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/workspace-list-item-view.js @@ -0,0 +1,26 @@ +define([ + 'backbone.marionette' +], function (Marionette) { + + return Marionette.ItemView.extend({ + + initialize: function (options) { + this.listenTo(options.app.state, 'change:selectedIndex', this.select); + }, + + onRender: function () { + this.select(); + }, + + 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') }); + } + + }); + +}); diff --git a/server/sonar-web/src/main/js/components/navigator/workspace-list-view.js b/server/sonar-web/src/main/js/components/navigator/workspace-list-view.js new file mode 100644 index 00000000000..2c9ef0def7d --- /dev/null +++ b/server/sonar-web/src/main/js/components/navigator/workspace-list-view.js @@ -0,0 +1,112 @@ +define([ + 'backbone.marionette' +], function (Marionette) { + + var $ = jQuery; + + return Marionette.CompositeView.extend({ + + ui: { + loadMore: '.js-more' + }, + + itemViewOptions: function () { + return { + app: this.options.app + }; + }, + + collectionEvents: { + 'reset': 'scrollToTop' + }, + + initialize: function (options) { + this.loadMoreThrottled = _.throttle(this.loadMore, 1000, {trailing: false}); + this.listenTo(options.app.state, 'change:maxResultsReached', this.toggleLoadMore); + this.listenTo(options.app.state, 'change:selectedIndex', this.scrollTo); + this.bindShortcuts(); + }, + + onClose: function () { + this.unbindScrollEvents(); + this.unbindShortcuts(); + }, + + toggleLoadMore: function () { + this.ui.loadMore.toggle(!this.options.app.state.get('maxResultsReached')); + }, + + bindScrollEvents: function () { + var that = this; + $(window).on('scroll.workspace-list-view', function () { + that.onScroll(); + }); + }, + + unbindScrollEvents: function () { + $(window).off('scroll.workspace-list-view'); + }, + + bindShortcuts: function () { + var that = this; + key('up', 'list', function () { + that.options.app.controller.selectPrev(); + return false; + }); + + key('down', 'list', function () { + that.options.app.controller.selectNext(); + return false; + }); + }, + + loadMore: function () { + if (!this.options.app.state.get('maxResultsReached')) { + var that = this; + this.unbindScrollEvents(); + this.options.app.controller.fetchNextPage().done(function () { + that.bindScrollEvents(); + }); + } + }, + + disablePointerEvents: function () { + clearTimeout(this.scrollTimer); + $('body').addClass('disabled-pointer-events'); + this.scrollTimer = setTimeout(function () { + $('body').removeClass('disabled-pointer-events'); + }, 250); + }, + + onScroll: function () { + this.disablePointerEvents(); + if ($(window).scrollTop() + $(window).height() >= this.ui.loadMore.offset().top) { + this.loadMoreThrottled(); + } + }, + + scrollToTop: function () { + this.$el.scrollParent().scrollTop(0); + }, + + scrollTo: function () { + var selected = this.collection.at(this.options.app.state.get('selectedIndex')); + if (selected == null) { + return; + } + var selectedView = this.children.findByModel(selected), + viewTop = selectedView.$el.offset().top, + viewBottom = selectedView.$el.offset().top + selectedView.$el.outerHeight(), + windowTop = $(window).scrollTop(), + windowBottom = windowTop + $(window).height(); + if (viewTop < windowTop) { + $(window).scrollTop(viewTop); + } + if (viewBottom > windowBottom) { + $(window).scrollTop($(window).scrollTop() - windowBottom + viewBottom); + } + } + + }); + +}); |