aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server/src/main/js/issues
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2014-03-25 15:18:29 +0600
committerStas Vilchik <vilchiks@gmail.com>2014-03-25 15:18:47 +0600
commit1c3cbb0af1a1294d2d3c672ee35f10008bee2ac7 (patch)
tree64cc0dff86514008bf4f45cdb54b68639b316fa7 /sonar-server/src/main/js/issues
parent6349cd3b899f37567637c78187f73071c1f64e3c (diff)
downloadsonarqube-1c3cbb0af1a1294d2d3c672ee35f10008bee2ac7.tar.gz
sonarqube-1c3cbb0af1a1294d2d3c672ee35f10008bee2ac7.zip
Move source outside from webapp
Diffstat (limited to 'sonar-server/src/main/js/issues')
-rw-r--r--sonar-server/src/main/js/issues/app.js402
-rw-r--r--sonar-server/src/main/js/issues/extra.js1242
2 files changed, 1644 insertions, 0 deletions
diff --git a/sonar-server/src/main/js/issues/app.js b/sonar-server/src/main/js/issues/app.js
new file mode 100644
index 00000000000..4a291cc45f7
--- /dev/null
+++ b/sonar-server/src/main/js/issues/app.js
@@ -0,0 +1,402 @@
+requirejs.config({
+ baseUrl: baseUrl + '/js',
+
+ paths: {
+ 'backbone': 'third-party/backbone',
+ 'backbone.marionette': 'third-party/backbone.marionette',
+ 'handlebars': 'third-party/handlebars',
+ 'moment': 'third-party/moment'
+ },
+
+ shim: {
+ 'backbone.marionette': {
+ deps: ['backbone'],
+ exports: 'Marionette'
+ },
+ 'backbone': {
+ exports: 'Backbone'
+ },
+ 'handlebars': {
+ exports: 'Handlebars'
+ },
+ 'moment': {
+ exports: 'moment'
+ }
+ }
+
+});
+
+requirejs(
+ [
+ 'backbone', 'backbone.marionette', 'handlebars', 'moment',
+ 'issues/extra',
+ 'navigator/filters/filter-bar',
+ 'navigator/filters/base-filters',
+ 'navigator/filters/checkbox-filters',
+ 'navigator/filters/choice-filters',
+ 'navigator/filters/ajax-select-filters',
+ 'navigator/filters/favorite-filters',
+ 'navigator/filters/range-filters',
+ 'navigator/filters/context-filters',
+ 'navigator/filters/read-only-filters',
+ 'navigator/filters/action-plan-filters',
+ 'navigator/filters/rule-filters',
+
+ 'common/handlebars-extensions'
+ ],
+ function (Backbone, Marionette, Handlebars, moment, Extra, FilterBar, BaseFilters, CheckboxFilterView,
+ ChoiceFilters, AjaxSelectFilters, FavoriteFilters, RangeFilters, ContextFilterView,
+ ReadOnlyFilterView, ActionPlanFilterView, RuleFilterView) {
+ Handlebars.registerPartial('detailInnerTemplate', jQuery('#issue-detail-inner-template').html());
+
+ var NavigatorApp = new Marionette.Application();
+
+
+ NavigatorApp.addRegions({
+ headerRegion: '.navigator-header',
+ filtersRegion: '.navigator-filters',
+ resultsRegion: '.navigator-results',
+ actionsRegion: '.navigator-actions',
+ detailsRegion: '.navigator-details'
+ });
+
+
+ // Adjust details region height
+ jQuery('.navigator-details').css('bottom', jQuery('#footer').outerHeight());
+
+
+ NavigatorApp.addInitializer(function () {
+ jQuery('html').addClass('navigator-page issues-page');
+
+ this.appState = new Extra.AppState();
+ window.SS.appState = this.appState;
+
+ this.state = new Backbone.Model({
+ query: ''
+ });
+
+ this.issues = new Extra.Issues();
+ this.issues.sorting = {
+ sort: 'UPDATE_DATE',
+ asc: false
+ };
+ this.issuesPage = 1;
+
+ this.filters = new BaseFilters.Filters();
+
+ this.favoriteFilter = new Extra.FavoriteFilter();
+ this.issuesHeaderView = new Extra.IssuesHeaderView({
+ app: this,
+ model: this.favoriteFilter
+ });
+ this.headerRegion.show(this.issuesHeaderView);
+
+ this.issuesView = new Extra.IssuesView({
+ app: this,
+ collection: this.issues
+ });
+ this.resultsRegion.show(this.issuesView);
+
+ this.issuesActionsView = new Extra.IssuesActionsView({
+ app: this,
+ collection: this.issues
+ });
+ this.actionsRegion.show(this.issuesActionsView);
+ });
+
+
+ NavigatorApp.addInitializer(function () {
+ var projectFilter = new BaseFilters.Filter({
+ name: window.SS.phrases.project,
+ property: 'componentRoots',
+ type: AjaxSelectFilters.ProjectFilterView,
+ enabled: true,
+ optional: false
+ });
+ this.filters.add(projectFilter);
+
+ this.filters.add([
+ new BaseFilters.Filter({
+ name: window.SS.phrases.severity,
+ property: 'severities',
+ type: ChoiceFilters.ChoiceFilterView,
+ enabled: true,
+ optional: false,
+ choices: {
+ 'BLOCKER': window.SS.phrases.severities.BLOCKER,
+ 'CRITICAL': window.SS.phrases.severities.CRITICAL,
+ 'MAJOR': window.SS.phrases.severities.MAJOR,
+ 'MINOR': window.SS.phrases.severities.MINOR,
+ 'INFO': window.SS.phrases.severities.INFO
+ },
+ choiceIcons: {
+ 'BLOCKER': 'severity-blocker',
+ 'CRITICAL': 'severity-critical',
+ 'MAJOR': 'severity-major',
+ 'MINOR': 'severity-minor',
+ 'INFO': 'severity-info'
+ }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.status,
+ property: 'statuses',
+ type: ChoiceFilters.ChoiceFilterView,
+ enabled: true,
+ optional: false,
+ choices: {
+ 'OPEN': window.SS.phrases.statuses.OPEN,
+ 'CONFIRMED': window.SS.phrases.statuses.CONFIRMED,
+ 'REOPENED': window.SS.phrases.statuses.REOPENED,
+ 'RESOLVED': window.SS.phrases.statuses.RESOLVED,
+ 'CLOSED': window.SS.phrases.statuses.CLOSED
+ },
+ choiceIcons: {
+ 'OPEN': 'status-open',
+ 'CONFIRMED': 'status-confirmed',
+ 'REOPENED': 'status-reopened',
+ 'RESOLVED': 'status-resolved',
+ 'CLOSED': 'status-closed'
+ }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.assignee,
+ property: 'assignees',
+ type: AjaxSelectFilters.AssigneeFilterView,
+ enabled: true,
+ optional: false,
+ choices: {
+ '!assigned': window.SS.phrases.unassigned
+ }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.resolution,
+ property: 'resolutions',
+ type: ChoiceFilters.ChoiceFilterView,
+ enabled: true,
+ optional: false,
+ choices: {
+ '!resolved': window.SS.phrases.resolutions.UNRESOLVED,
+ 'FALSE-POSITIVE': window.SS.phrases.resolutions['FALSE-POSITIVE'],
+ 'FIXED': window.SS.phrases.resolutions.FIXED,
+ 'REMOVED': window.SS.phrases.resolutions.REMOVED
+ }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.actionPlan,
+ property: 'actionPlans',
+ type: ActionPlanFilterView,
+ enabled: false,
+ optional: true,
+ projectFilter: projectFilter,
+ choices: {
+ '!planned': window.SS.phrases.unplanned
+ }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.created,
+ propertyFrom: 'createdAfter',
+ propertyTo: 'createdBefore',
+ type: RangeFilters.DateRangeFilterView,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.createdAt,
+ property: 'createdAt',
+ type: ReadOnlyFilterView,
+ enabled: false,
+ optional: true,
+ format: function(value) { return moment(value).format('YYYY-MM-DD HH:mm'); }
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.reporter,
+ property: 'reporters',
+ type: AjaxSelectFilters.ReporterFilterView,
+ enabled: false,
+ optional: true
+ }),
+
+ new BaseFilters.Filter({
+ name: window.SS.phrases.rule,
+ property: 'rules',
+ type: RuleFilterView,
+ enabled: false,
+ optional: true
+ })
+
+ ]);
+
+
+ this.filterBarView = new Extra.IssuesFilterBarView({
+ app: this,
+ collection: this.filters,
+ extra: {
+ sort: '',
+ asc: false
+ }
+ });
+
+ this.filtersRegion.show(this.filterBarView);
+ });
+
+
+ NavigatorApp.addInitializer(function () {
+ var app = this;
+
+ jQuery.when(this.appState.fetch()).done(function () {
+
+ if (app.appState.get('favorites')) {
+ app.filters.unshift(
+ new BaseFilters.Filter({
+ type: Extra.IssuesFavoriteFilterView,
+ enabled: true,
+ optional: false,
+ choices: app.appState.get('favorites'),
+ manageUrl: '/issues/manage'
+ })
+ );
+ }
+
+ app.router = new Extra.IssuesRouter({
+ app: app
+ });
+ Backbone.history.start();
+
+ app.favoriteFilter.on('change:query', function (model, query) {
+ app.router.navigate(query, { trigger: true, replace: true });
+ });
+ });
+ });
+
+
+ NavigatorApp.addInitializer(function () {
+ var app = this;
+
+ window.onBulkIssues = function () {
+ app.fetchFirstPage();
+ jQuery('.ui-dialog, .ui-widget-overlay').remove();
+ };
+
+ window.onSaveAs = window.onCopy = window.onEdit = function (id) {
+ jQuery('#modal').dialog('close');
+ app.appState.fetch();
+
+ var filter = new Extra.FavoriteFilter({ id: id });
+ filter.fetch({
+ success: function () {
+ app.state.set('search', false);
+ app.favoriteFilter.set(filter.toJSON());
+ app.fetchFirstPage();
+ }
+ });
+ };
+ });
+
+
+ NavigatorApp.getQuery = function (withoutId) {
+ var query = this.filterBarView.getQuery();
+ if (!withoutId && this.favoriteFilter.id) {
+ query['id'] = this.favoriteFilter.id;
+ }
+ return query;
+ };
+
+
+ NavigatorApp.storeQuery = function (query, sorting) {
+ if (sorting) {
+ _.extend(query, {
+ sort: sorting.sort,
+ asc: '' + sorting.asc
+ });
+ }
+
+ var queryString = _.map(query,function (v, k) {
+ return [k, encodeURIComponent(v)].join('=');
+ }).join('|');
+ this.router.navigate(queryString, { replace: true });
+ };
+
+
+ NavigatorApp.restoreSorting = function (query) {
+ var sort = _.findWhere(query, { key: 'sort' }),
+ asc = _.findWhere(query, { key: 'asc' });
+
+ if (sort && asc) {
+ this.issues.sorting = {
+ sort: sort.value,
+ sortText: jQuery('[data-sort=' + sort.value + ']:first').text(),
+ asc: asc.value === 'true'
+ }
+ }
+ };
+
+
+ NavigatorApp.fetchIssues = function (firstPage) {
+ var query = this.getQuery(),
+ fetchQuery = _.extend({
+ pageIndex: this.issuesPage
+ }, query);
+
+ if (this.issues.sorting) {
+ _.extend(fetchQuery, {
+ sort: this.issues.sorting.sort,
+ asc: this.issues.sorting.asc
+ });
+ }
+
+ _.extend(fetchQuery, {
+ hideRules: true
+ });
+
+ if (this.favoriteFilter.id) {
+ query['id'] = this.favoriteFilter.id;
+ fetchQuery['id'] = this.favoriteFilter.id;
+ }
+
+ this.storeQuery(query, this.issues.sorting);
+
+ var that = this;
+ this.issuesView.$el.addClass('navigator-fetching');
+ if (firstPage) {
+ this.issues.fetch({
+ data: fetchQuery,
+ success: function () {
+ that.issuesView.$el.removeClass('navigator-fetching');
+ }
+ });
+ this.detailsRegion.reset();
+ } else {
+ this.issues.fetch({
+ data: fetchQuery,
+ remove: false,
+ success: function () {
+ that.issuesView.$el.removeClass('navigator-fetching');
+ }
+ });
+ }
+ };
+
+
+ NavigatorApp.fetchFirstPage = function () {
+ this.issuesPage = 1;
+ this.fetchIssues(true);
+ };
+
+
+ NavigatorApp.fetchNextPage = function () {
+ if (this.issuesPage < this.issues.paging.pages) {
+ this.issuesPage++;
+ this.fetchIssues(false);
+ }
+ };
+
+ NavigatorApp.start();
+
+ });
diff --git a/sonar-server/src/main/js/issues/extra.js b/sonar-server/src/main/js/issues/extra.js
new file mode 100644
index 00000000000..66fa821b014
--- /dev/null
+++ b/sonar-server/src/main/js/issues/extra.js
@@ -0,0 +1,1242 @@
+define(
+ [
+ 'backbone', 'backbone.marionette',
+ '../navigator/filters/filter-bar',
+ 'navigator/filters/base-filters',
+ 'navigator/filters/favorite-filters',
+ 'navigator/filters/read-only-filters'
+ ],
+ function (Backbone, Marionette, FilterBarView, BaseFilters, FavoriteFiltersModule, ReadOnlyFilterView) {
+
+ var AppState = Backbone.Model.extend({
+
+ defaults: {
+ canManageFilters: false,
+ canBulkChange: false
+ },
+
+
+ url: function () {
+ return baseUrl + '/api/issue_filters/app';
+ }
+
+ });
+
+
+ var Issue = Backbone.Model.extend({
+
+ url: function () {
+ return baseUrl + '/api/issues/show?key=' + this.get('key');
+ },
+
+
+ parse: function (r) {
+ return r.issue ? r.issue : r;
+ }
+
+ });
+
+
+ var Issues = Backbone.Collection.extend({
+ model: Issue,
+
+
+ url: function () {
+ return baseUrl + '/api/issues/search';
+ },
+
+
+ parse: function (r) {
+
+ function find(source, key, keyField) {
+ var searchDict = {};
+ searchDict[keyField || 'key'] = key;
+ return _.findWhere(source, searchDict) || key;
+ }
+
+ this.paging = r.paging;
+ this.maxResultsReached = r.maxResultsReached;
+
+ return r.issues.map(function (issue) {
+ var component = find(r.components, issue.component),
+ project = find(r.projects, issue.project),
+ rule = find(r.rules, issue.rule);
+
+ if (component) {
+ _.extend(issue, {
+ componentLongName: component.longName,
+ componentQualifier: component.qualifier
+ });
+ }
+
+ if (project) {
+ _.extend(issue, {
+ projectLongName: project.longName
+ });
+ }
+
+ if (rule) {
+ _.extend(issue, {
+ ruleName: rule.name
+ });
+ }
+
+ return issue;
+ });
+
+ }
+ });
+
+
+ var FavoriteFilter = Backbone.Model.extend({
+
+ url: function () {
+ return baseUrl + '/api/issue_filters/show/' + this.get('id');
+ },
+
+
+ parse: function (r) {
+ return r.filter ? r.filter : r;
+ }
+ });
+
+
+ var FavoriteFilters = Backbone.Collection.extend({
+ model: FavoriteFilter,
+
+
+ url: function () {
+ return baseUrl + '/api/issue_filters/favorites';
+ },
+
+
+ parse: function (r) {
+ return r.favoriteFilters;
+ }
+ });
+
+
+ var Rule = Backbone.Model.extend({
+
+ url: function () {
+ return baseUrl + '/api/rules/show/?key=' + this.get('key');
+ },
+
+
+ parse: function (r) {
+ return r.rule ? r.rule : r;
+ }
+ });
+
+
+ var ActionPlans = Backbone.Collection.extend({
+
+ url: function () {
+ return baseUrl + '/api/action_plans/search';
+ },
+
+
+ parse: function (r) {
+ return r.actionPlans;
+ }
+
+ });
+
+
+ var IssueView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-template').html() || ''),
+ tagName: 'li',
+
+
+ ui: {
+ component: '.component'
+ },
+
+
+ events: {
+ 'click': 'showDetails'
+ },
+
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+
+ showDetails: function () {
+ this.$el.parent().children().removeClass('active');
+ this.$el.addClass('active');
+
+ var that = this,
+ app = this.options.app,
+ detailView = new IssueDetailView({
+ model: this.model
+ }),
+ showCallback = function () {
+ jQuery('.navigator-details').removeClass('navigator-fetching');
+ app.detailsRegion.show(detailView);
+ };
+
+ jQuery('.navigator-details').empty().addClass('navigator-fetching');
+ jQuery.when(detailView.model.fetch()).done(function () {
+ if (that.model.get('status') !== 'CLOSED') {
+ that.fetchSource(detailView, showCallback);
+ } else {
+ showCallback();
+ }
+
+ });
+ },
+
+
+ fetchSource: function (view, callback) {
+ var line = this.model.get('line') || 0,
+ from = line >= 10 ? line - 10 : 0,
+ to = line + 30;
+
+ return jQuery
+ .ajax({
+ type: 'GET',
+ url: baseUrl + '/api/sources/show',
+ data: {
+ key: this.model.get('component'),
+ from: from,
+ to: to,
+ format: 'json'
+ }
+ })
+ .done(function (r) {
+ if (_.isObject(r) && r.source) {
+ view.source = r.source;
+ }
+ if (_.isObject(r) && r.scm) {
+ view.scm = r.scm;
+ }
+ })
+ .always(callback);
+ },
+
+
+ serializeData: function () {
+ var projectFilter = this.options.app.filters.findWhere({ property: 'componentRoots' }),
+ singleProject = _.isArray(projectFilter.get('value')) && projectFilter.get('value').length === 1;
+
+ return _.extend({
+ singleProject: singleProject
+ }, this.model.toJSON());
+ }
+ });
+
+
+ var NoIssuesView = Marionette.ItemView.extend({
+ tagName: 'li',
+ className: 'navigator-results-no-results',
+ template: Handlebars.compile(jQuery('#no-issues-template').html() || '')
+ });
+
+
+ var IssuesView = Marionette.CollectionView.extend({
+ tagName: 'ol',
+ className: 'navigator-results-list',
+ itemView: IssueView,
+ emptyView: NoIssuesView,
+
+
+ itemViewOptions: function () {
+ return {
+ issuesView: this,
+ app: this.options.app
+ };
+ },
+
+
+ onRender: function () {
+ var that = this,
+ $scrollEl = jQuery('.navigator-results'),
+ scrollEl = $scrollEl.get(0),
+ onScroll = function () {
+ if (scrollEl.offsetHeight + scrollEl.scrollTop >= scrollEl.scrollHeight) {
+ that.options.app.fetchNextPage();
+ }
+ },
+ throttledScroll = _.throttle(onScroll, 300);
+ $scrollEl.off('scroll').on('scroll', throttledScroll);
+ },
+
+
+ onAfterItemAdded: function () {
+ var showLimitNotes = this.collection.maxResultsReached != null && this.collection.maxResultsReached;
+ jQuery('.navigator').toggleClass('navigator-with-notes', showLimitNotes);
+ jQuery('.navigator-notes').toggle(showLimitNotes);
+ },
+
+
+ close: function () {
+ var scrollEl = jQuery('.navigator-results');
+ scrollEl.off('scroll');
+ Marionette.CollectionView.prototype.close.call(this);
+ }
+
+ });
+
+
+ var IssuesActionsView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issues-actions-template').html() || ''),
+
+
+ collectionEvents: {
+ 'sync': 'render'
+ },
+
+
+ events: {
+ 'click .navigator-actions-order': 'toggleOrderChoices',
+ 'click .navigator-actions-order-choices': 'sort',
+ 'click .navigator-actions-bulk': 'bulkChange'
+ },
+
+
+ ui: {
+ orderChoices: '.navigator-actions-order-choices'
+ },
+
+
+ onRender: function () {
+ if (!this.collection.sorting.sortText) {
+ this.collection.sorting.sortText = this.$('[data-sort=' + this.collection.sorting.sort + ']:first').text();
+ this.$('.navigator-actions-ordered-by').text(this.collection.sorting.sortText);
+ }
+ },
+
+
+ toggleOrderChoices: function (e) {
+ e.stopPropagation();
+ this.ui.orderChoices.toggleClass('open');
+ if (this.ui.orderChoices.is('.open')) {
+ var that = this;
+ jQuery('body').on('click.issues_actions', function () {
+ that.ui.orderChoices.removeClass('open');
+ });
+ }
+ },
+
+
+ sort: function (e) {
+ e.stopPropagation();
+ this.ui.orderChoices.removeClass('open');
+ jQuery('body').off('click.issues_actions');
+ var el = jQuery(e.target),
+ sort = el.data('sort'),
+ asc = el.data('asc');
+
+ if (sort != null && asc != null) {
+ this.collection.sorting = {
+ sort: sort,
+ sortText: el.text(),
+ asc: asc
+ };
+ this.options.app.fetchFirstPage();
+ }
+ },
+
+
+ bulkChange: function(e) {
+ e.preventDefault();
+ openModalWindow(jQuery(e.currentTarget).prop('href'), {});
+ },
+
+
+ serializeData: function () {
+ var data = Marionette.ItemView.prototype.serializeData.apply(this, arguments);
+ return _.extend(data || {}, {
+ paging: this.collection.paging,
+ sorting: this.collection.sorting,
+ maxResultsReached: this.collection.maxResultsReached,
+ appState: window.SS.appState.toJSON(),
+ query: (Backbone.history.fragment || '').replace(/\|/g, '&')
+ });
+ }
+ });
+
+
+ var IssuesFilterBarView = FilterBarView.extend({
+
+ collectionEvents: {
+ 'change:enabled': 'changeEnabled'
+ },
+
+
+ events: {
+ 'click .navigator-filter-submit': 'search'
+ },
+
+
+ getQuery: function () {
+ var query = {};
+ this.collection.each(function (filter) {
+ _.extend(query, filter.view.formatValue());
+ });
+ return query;
+ },
+
+
+ onAfterItemAdded: function (itemView) {
+ if (itemView.model.get('type') === FavoriteFiltersModule.FavoriteFilterView ||
+ itemView.model.get('type') === IssuesFavoriteFilterView) {
+ jQuery('.navigator-header').addClass('navigator-header-favorite');
+ }
+ },
+
+
+ addMoreCriteriaFilter: function() {
+ var readOnlyFilters = this.collection.where({ type: ReadOnlyFilterView }),
+ disabledFilters = _.difference(this.collection.where({ enabled: false }), readOnlyFilters);
+ this.moreCriteriaFilter = new BaseFilters.Filter({
+ type: require('navigator/filters/more-criteria-filters').MoreCriteriaFilterView,
+ enabled: true,
+ optional: false,
+ filters: disabledFilters
+ });
+ this.collection.add(this.moreCriteriaFilter);
+ },
+
+
+ changeEnabled: function () {
+ var disabledFilters = _.reject(this.collection.where({ enabled: false }), function (filter) {
+ return filter.get('type') === require('navigator/filters/more-criteria-filters').MoreCriteriaFilterView ||
+ filter.get('type') === ReadOnlyFilterView;
+ });
+
+ if (disabledFilters.length === 0) {
+ this.moreCriteriaFilter.set({ enabled: false }, { silent: true });
+ } else {
+ this.moreCriteriaFilter.set({ enabled: true }, { silent: true });
+ }
+ this.moreCriteriaFilter.set({ filters: disabledFilters }, { silent: true });
+ this.moreCriteriaFilter.trigger('change:filters');
+ },
+
+
+ search: function () {
+ this.options.app.state.set({
+ query: this.options.app.getQuery(),
+ search: true
+ });
+ this.options.app.fetchFirstPage();
+ },
+
+
+ fetchNextPage: function () {
+ this.options.app.fetchNextPage();
+ }
+
+ });
+
+
+ var IssuesHeaderView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issues-header-template').html() || ''),
+
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+
+ events: {
+ 'click #issues-new-search': 'newSearch',
+ 'click #issues-filter-save-as': 'saveAs',
+ 'click #issues-filter-save': 'save',
+ 'click #issues-filter-copy': 'copy',
+ 'click #issues-filter-edit': 'edit'
+ },
+
+
+ initialize: function (options) {
+ Marionette.ItemView.prototype.initialize.apply(this, arguments);
+ this.listenTo(options.app.state, 'change', this.render);
+ },
+
+
+ newSearch: function () {
+ this.model.clear();
+ this.options.app.router.navigate('resolved=false', { trigger: true, replace: true });
+ },
+
+
+ saveAs: function () {
+ var url = baseUrl + '/issues/save_as_form?' + (Backbone.history.fragment || '').replace(/\|/g, '&');
+ openModalWindow(url, {});
+ },
+
+
+ save: function () {
+ var that = this;
+ url = baseUrl + '/issues/save/' + this.model.id + '?' + (Backbone.history.fragment || '').replace(/\|/g, '&');
+ jQuery.ajax({
+ type: 'POST',
+ url: url
+ }).done(function () {
+ that.options.app.state.set('search', false);
+ });
+ },
+
+
+ copy: function () {
+ var url = baseUrl + '/issues/copy_form/' + this.model.id;
+ openModalWindow(url, {});
+ },
+
+
+ edit: function () {
+ var url = baseUrl + '/issues/edit_form/' + this.model.id;
+ openModalWindow(url, {});
+ },
+
+
+ serializeData: function () {
+ return _.extend({
+ canSave: this.model.id && this.options.app.state.get('search'),
+ appState: window.SS.appState.toJSON(),
+ currentUser: window.SS.currentUser
+ }, this.model.toJSON());
+ }
+
+ });
+
+
+ var IssueDetailCommentFormView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-comment-form-template').html() || ''),
+
+
+ ui: {
+ textarea: '#issue-comment-text',
+ cancelButton: '#issue-comment-cancel',
+ submitButton: '#issue-comment-submit'
+ },
+
+
+ events: {
+ 'keyup #issue-comment-text': 'toggleSubmit',
+ 'click #issue-comment-cancel': 'cancel',
+ 'click #issue-comment-submit': 'submit'
+ },
+
+
+ onDomRefresh: function () {
+ this.ui.textarea.focus();
+ },
+
+
+ toggleSubmit: function () {
+ this.ui.submitButton.prop('disabled', this.ui.textarea.val().length === 0);
+ },
+
+
+ cancel: function () {
+ this.options.detailView.updateAfterAction(false);
+ },
+
+
+ submit: function () {
+ var that = this,
+ text = this.ui.textarea.val(),
+ update = this.model && this.model.has('key'),
+ url = baseUrl + '/api/issues/' + (update ? 'edit_comment' : 'add_comment'),
+ data = { text: text };
+
+ if (update) {
+ data.key = this.model.get('key');
+ } else {
+ data.issue = this.options.issue.get('key');
+ }
+
+ this.options.detailView.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: url,
+ data: data
+ })
+ .done(function () {
+ that.options.detailView.updateAfterAction(true);
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.options.detailView.hideActionSpinner();
+ });
+ }
+ });
+
+
+ var IssueDetailSetSeverityFormView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-set-severity-form-template').html() || ''),
+
+
+ ui: {
+ select: '#issue-set-severity-select'
+ },
+
+
+ events: {
+ 'click #issue-set-severity-cancel': 'cancel',
+ 'click #issue-set-severity-submit': 'submit'
+ },
+
+
+ onRender: function () {
+ var format = function(state) {
+ if (!state.id) return state.text; // optgroup
+ return '<i class="icon-severity-' + state.id.toLowerCase() + '"></i> ' + state.text;
+ }
+
+ this.ui.select.select2({
+ minimumResultsForSearch: 100,
+ formatResult: format,
+ formatSelection: format,
+ escapeMarkup: function(m) { return m; }
+ });
+ },
+
+
+ cancel: function () {
+ this.options.detailView.updateAfterAction(false);
+ },
+
+
+ submit: function () {
+ var that = this;
+
+ this.options.detailView.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/set_severity',
+ data: {
+ issue: this.options.issue.get('key'),
+ severity: this.ui.select.val()
+ }
+ })
+ .done(function () {
+ that.options.detailView.updateAfterAction(true);
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.options.detailView.hideActionSpinner();
+ });
+ }
+ });
+
+
+ var IssueDetailAssignFormView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-assign-form-template').html() || ''),
+
+
+ ui: {
+ select: '#issue-assignee-select'
+ },
+
+
+ events: {
+ 'click #issue-assign-cancel': 'cancel',
+ 'click #issue-assign-submit': 'submit'
+ },
+
+
+ onRender: function () {
+ var currentUser = window.SS.currentUser,
+ assignee = this.options.issue.get('assignee'),
+ additionalChoices = [];
+
+ if (!assignee || currentUser !== assignee) {
+ additionalChoices.push({
+ id: currentUser,
+ text: window.SS.phrases.assignedToMe
+ });
+ }
+
+ if (!!assignee) {
+ additionalChoices.push({
+ id: '',
+ text: window.SS.phrases.unassigned
+ });
+ }
+
+ var select2Options = {
+ allowClear: false,
+ width: '250px',
+ formatNoMatches: function () {
+ return window.SS.phrases.select2.noMatches;
+ },
+ formatSearching: function () {
+ return window.SS.phrases.select2.searching;
+ },
+ formatInputTooShort: function () {
+ return window.SS.phrases.select2.tooShort;
+ }
+ };
+
+ if (additionalChoices.length > 0) {
+ select2Options.minimumInputLength = 0;
+ select2Options.query = function (query) {
+ if (query.term.length == 0) {
+ query.callback({ results: additionalChoices });
+ } else if (query.term.length >= 2) {
+ jQuery.ajax({
+ url: baseUrl + '/api/users/search?f=s2',
+ data: { s: query.term },
+ dataType: 'jsonp'
+ }).done(function (data) {
+ query.callback(data);
+ });
+ }
+ }
+ } else {
+ select2Options.minimumInputLength = 2;
+ select2Options.ajax = {
+ quietMillis: 300,
+ url: baseUrl + '/api/users/search?f=s2',
+ data: function (term, page) {
+ return {s: term, p: page}
+ },
+ results: function (data) {
+ return { more: data.more, results: data.results }
+ }
+ };
+ }
+
+ this.ui.select.select2(select2Options).select2('open');
+ },
+
+
+ cancel: function () {
+ this.options.detailView.updateAfterAction(false);
+ },
+
+
+ submit: function () {
+ var that = this;
+
+ this.options.detailView.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/assign',
+ data: {
+ issue: this.options.issue.get('key'),
+ assignee: this.ui.select.val()
+ }
+ })
+ .done(function () {
+ that.options.detailView.updateAfterAction(true);
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.options.detailView.hideActionSpinner();
+ });
+ }
+ });
+
+
+ var IssueDetailPlanFormView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-plan-form-template').html() || ''),
+
+
+ collectionEvents: {
+ 'reset': 'render'
+ },
+
+
+ ui: {
+ select: '#issue-detail-plan-select'
+ },
+
+
+ events: {
+ 'click #issue-plan-cancel': 'cancel',
+ 'click #issue-plan-submit': 'submit'
+ },
+
+
+ onRender: function () {
+ this.ui.select.select2({
+ width: '250px',
+ minimumResultsForSearch: 100
+ });
+
+ this.$('.error a')
+ .prop('href', baseUrl + '/action_plans/index/' + this.options.issue.get('project'));
+ },
+
+
+ cancel: function () {
+ this.options.detailView.updateAfterAction(false);
+ },
+
+
+ submit: function () {
+ var that = this,
+ plan = this.ui.select.val();
+
+ this.options.detailView.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/plan',
+ data: {
+ issue: this.options.issue.get('key'),
+ plan: plan === '#unplan' ? '' : plan
+ }
+ })
+ .done(function () {
+ that.options.detailView.updateAfterAction(true);
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.options.detailView.hideActionSpinner();
+ });
+ },
+
+
+ serializeData: function () {
+ return {
+ items: this.collection.toJSON(),
+ issue: this.options.issue.toJSON()
+ }
+ }
+ });
+
+
+ var IssueDetailRuleView = Marionette.ItemView.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-rule-template').html() || ''),
+ className: 'rule-desc',
+ modelEvents: { 'change': 'render' },
+
+
+ serializeData: function () {
+ return _.extend({
+ characteristic: this.options.issue.get('characteristic'),
+ subCharacteristic: this.options.issue.get('subCharacteristic')
+ }, this.model.toJSON());
+ }
+ });
+
+
+ var IssueDetailView = Marionette.Layout.extend({
+ template: Handlebars.compile(jQuery('#issue-detail-template').html() || ''),
+
+
+ regions: {
+ formRegion: '.code-issue-form',
+ ruleRegion: '#tab-issue-rule'
+ },
+
+
+ events: {
+ 'click .code-issue-toggle': 'toggleCollapsed',
+
+ 'click [href=#tab-issue-rule]': 'fetchRule',
+
+ 'click #issue-comment': 'comment',
+ 'click .issue-comment-edit': 'editComment',
+ 'click .issue-comment-delete': 'deleteComment',
+ 'click .issue-transition': 'transition',
+ 'click #issue-set-severity': 'setSeverity',
+ 'click #issue-assign': 'assign',
+ 'click #issue-assign-to-me': 'assignToMe',
+ 'click #issue-plan': 'plan',
+ 'click .issue-action': 'action'
+ },
+
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+
+ onRender: function () {
+ this.$('.code-issue-details').tabs();
+ this.$('.code-issue-form').hide();
+ this.rule = new Rule({ key: this.model.get('rule') });
+ this.ruleRegion.show(new IssueDetailRuleView({
+ model: this.rule,
+ issue: this.model
+ }));
+ this.initReferenceLinks();
+ },
+
+
+ initReferenceLinks: function () {
+ var sourcesId = 'sources_' + this.model.get('key');
+ this.$('#' + sourcesId).on('click', 'span.sym', { id: sourcesId }, highlight_usages);
+ },
+
+
+ onDomRefresh: function () {
+ var sourceTitleHeight = this.$('.source_title').outerHeight();
+ jQuery('.navigator-details').css('padding-top', (sourceTitleHeight + 10) + 'px');
+ },
+
+
+ onClose: function () {
+ if (this.ruleRegion) {
+ this.ruleRegion.reset();
+ }
+ },
+
+
+ resetIssue: function (options) {
+ var key = this.model.get('key');
+ this.model.clear({ silent: true });
+ this.model.set({ key: key }, { silent: true });
+ return this.model.fetch(options);
+ },
+
+
+ toggleCollapsed: function () {
+ this.$('.code-issue').toggleClass('code-issue-collapsed');
+ this.fetchRule();
+ },
+
+
+ fetchRule: function () {
+ var that = this;
+ if (!this.rule.has('name')) {
+ this.$('#tab-issue-rule').addClass('navigator-fetching');
+ this.rule.fetch({
+ success: function () {
+ that.$('#tab-issue-rule').removeClass('navigator-fetching');
+ }
+ });
+ }
+ },
+
+
+ showActionView: function (view) {
+ this.$('.code-issue-actions').hide();
+ this.$('.code-issue-form').show();
+ this.formRegion.show(view);
+ },
+
+
+ showActionSpinner: function () {
+ this.$('.code-issue-actions').addClass('navigator-fetching');
+ },
+
+
+ hideActionSpinner: function () {
+ this.$('.code-issue-actions').removeClass('navigator-fetching');
+ },
+
+
+ updateAfterAction: function (fetch) {
+ var that = this;
+
+ that.formRegion.reset();
+ that.$('.code-issue-actions').show();
+ that.$('.code-issue-form').hide();
+ that.$('[data-comment-key]').show();
+
+ if (fetch) {
+ jQuery.when(this.resetIssue()).done(function () {
+ that.hideActionSpinner();
+ });
+ }
+ },
+
+
+ comment: function () {
+ var commentFormView = new IssueDetailCommentFormView({
+ issue: this.model,
+ detailView: this
+ });
+ this.showActionView(commentFormView);
+ },
+
+
+ editComment: function (e) {
+ var commentEl = jQuery(e.target).closest('[data-comment-key]'),
+ commentKey = commentEl.data('comment-key'),
+ comment = _.findWhere(this.model.get('comments'), { key: commentKey });
+
+ commentEl.hide();
+
+ var commentFormView = new IssueDetailCommentFormView({
+ model: new Backbone.Model(comment),
+ issue: this.model,
+ detailView: this
+ });
+ this.showActionView(commentFormView);
+ },
+
+
+ deleteComment: function (e) {
+ var that = this,
+ commentKey = jQuery(e.target).closest('[data-comment-key]').data('comment-key'),
+ confirmMsg = jQuery(e.target).data('confirm-msg');
+
+ if (confirm(confirmMsg)) {
+ this.showActionSpinner();
+
+ jQuery.ajax({
+ type: "POST",
+ url: baseUrl + "/issue/delete_comment?id=" + commentKey
+ })
+ .done(function () {
+ that.updateAfterAction(true);
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.hideActionSpinner();
+ });
+ }
+ },
+
+
+ transition: function (e) {
+ var that = this;
+
+ this.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/do_transition',
+ data: {
+ issue: this.model.get('key'),
+ transition: jQuery(e.target).data('transition')
+ }
+ })
+ .done(function () {
+ that.resetIssue();
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.hideActionSpinner();
+ });
+ },
+
+
+ setSeverity: function () {
+ var setSeverityFormView = new IssueDetailSetSeverityFormView({
+ issue: this.model,
+ detailView: this
+ });
+ this.showActionView(setSeverityFormView);
+ },
+
+
+ assign: function () {
+ var assignFormView = new IssueDetailAssignFormView({
+ issue: this.model,
+ detailView: this
+ });
+ this.showActionView(assignFormView);
+ },
+
+
+ assignToMe: function () {
+ var that = this;
+
+ this.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/assign',
+ data: {
+ issue: this.model.get('key'),
+ assignee: window.SS.currentUser
+ }
+ })
+ .done(function () {
+ that.resetIssue();
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.hideActionSpinner();
+ });
+ },
+
+
+ plan: function () {
+ var that = this,
+ actionPlans = new ActionPlans(),
+ planFormView = new IssueDetailPlanFormView({
+ collection: actionPlans,
+ issue: this.model,
+ detailView: this
+ });
+
+ this.showActionSpinner();
+
+ actionPlans.fetch({
+ reset: true,
+ data: { project: this.model.get('project') },
+ success: function () {
+ that.hideActionSpinner();
+ that.showActionView(planFormView);
+ }
+ });
+ },
+
+ action: function (e) {
+ var that = this,
+ actionKey = jQuery(e.target).data('action');
+
+ this.showActionSpinner();
+
+ jQuery.ajax({
+ type: 'POST',
+ url: baseUrl + '/api/issues/do_action',
+ data: {
+ issue: this.model.get('key'),
+ actionKey: actionKey
+ }
+ })
+ .done(function () {
+ that.resetIssue();
+ })
+ .fail(function (r) {
+ alert(r.responseJSON.errors ? _.pluck(r.responseJSON.errors, 'msg').join(' ') : r);
+ that.hideActionSpinner();
+ });
+ },
+
+
+ serializeData: function () {
+ return _.extend({
+ source: this.source,
+ scm: this.scm
+ }, this.model.toJSON());
+ }
+
+ });
+
+
+ var IssuesDetailsFavoriteFilterView = FavoriteFiltersModule.DetailsFavoriteFilterView.extend({
+ template: Handlebars.compile(jQuery('#issues-details-favorite-filter-template').html() || ''),
+
+
+ applyFavorite: function (e) {
+ var id = $j(e.target).data('id'),
+ filter = new FavoriteFilter({ id: id }),
+ app = this.options.filterView.options.app;
+
+ filter.fetch({
+ success: function () {
+ app.state.set('search', false);
+ app.favoriteFilter.clear({ silent: true });
+ app.favoriteFilter.set(filter.toJSON());
+ }
+ });
+
+ this.options.filterView.hideDetails();
+ },
+
+
+ serializeData: function () {
+ return _.extend({}, this.model.toJSON(), {
+ items: _.sortBy(this.model.get('choices'), function(item) {
+ return item.name.toLowerCase();
+ })
+ });
+ }
+ });
+
+
+ var IssuesFavoriteFilterView = FavoriteFiltersModule.FavoriteFilterView.extend({
+
+ initialize: function () {
+ BaseFilters.BaseFilterView.prototype.initialize.call(this, {
+ detailsView: IssuesDetailsFavoriteFilterView
+ });
+
+ this.listenTo(window.SS.appState, 'change:favorites', this.updateFavorites);
+ },
+
+
+ updateFavorites: function () {
+ this.model.set('choices', window.SS.appState.get('favorites'));
+ this.render();
+ }
+ });
+
+
+ var IssuesRouter = Backbone.Router.extend({
+
+ routes: {
+ '': 'emptyQuery',
+ ':query': 'index'
+ },
+
+
+ initialize: function (options) {
+ this.app = options.app;
+ },
+
+
+ parseQuery: function (query, separator) {
+ return (query || '').split(separator || '|').map(function (t) {
+ var tokens = t.split('=');
+ return {
+ key: tokens[0],
+ value: decodeURIComponent(tokens[1])
+ }
+ });
+ },
+
+
+ emptyQuery: function () {
+ this.navigate('resolved=false', { trigger: true, replace: true });
+ },
+
+
+ index: function (query) {
+ var params = this.parseQuery(query);
+
+ var idObj = _.findWhere(params, { key: 'id' });
+ if (idObj) {
+ var that = this,
+ f = this.app.favoriteFilter;
+ this.app.canSave = false;
+ f.set('id', idObj.value);
+ f.fetch({
+ success: function () {
+ params = _.extend({}, that.parseQuery(f.get('query')), params);
+ that.loadResults(params);
+ }
+ });
+ } else {
+ this.loadResults(params);
+ }
+ },
+
+
+ loadResults: function (params) {
+ this.app.filterBarView.restoreFromQuery(params);
+ this.app.restoreSorting(params);
+ this.app.fetchFirstPage();
+ }
+
+ });
+
+
+ /*
+ * Export public classes
+ */
+
+ return {
+ AppState: AppState,
+ Issue: Issue,
+ Issues: Issues,
+ FavoriteFilter: FavoriteFilter,
+ FavoriteFilters: FavoriteFilters,
+ IssueView: IssueView,
+ IssuesView: IssuesView,
+ IssuesActionsView: IssuesActionsView,
+ IssuesFilterBarView: IssuesFilterBarView,
+ IssuesHeaderView: IssuesHeaderView,
+ IssuesFavoriteFilterView: IssuesFavoriteFilterView,
+ IssueDetailView: IssueDetailView,
+ IssuesRouter: IssuesRouter
+ };
+
+ });