]> source.dussan.org Git - sonarqube.git/commitdiff
Move javascript files into module dirs
authorStas Vilchik <vilchiks@gmail.com>
Thu, 27 Feb 2014 17:22:08 +0000 (18:22 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 27 Feb 2014 17:22:15 +0000 (18:22 +0100)
22 files changed:
sonar-server/src/main/webapp/WEB-INF/app/views/groups/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/roles/global.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/users/index.html.erb
sonar-server/src/main/webapp/javascripts/build.js
sonar-server/src/main/webapp/javascripts/common/handlebars-extensions.js [new file with mode: 0644]
sonar-server/src/main/webapp/javascripts/common/select-list.js [new file with mode: 0644]
sonar-server/src/main/webapp/javascripts/handlebars-extensions.js [deleted file]
sonar-server/src/main/webapp/javascripts/issues-extra.js [deleted file]
sonar-server/src/main/webapp/javascripts/issues.js [deleted file]
sonar-server/src/main/webapp/javascripts/issues/app.js [new file with mode: 0644]
sonar-server/src/main/webapp/javascripts/issues/extra.js [new file with mode: 0644]
sonar-server/src/main/webapp/javascripts/measures.js [deleted file]
sonar-server/src/main/webapp/javascripts/measures/app.js [new file with mode: 0644]
sonar-server/src/main/webapp/javascripts/quality-gate-build-config.js [deleted file]
sonar-server/src/main/webapp/javascripts/quality-gate/app.coffee
sonar-server/src/main/webapp/javascripts/quality-gate/app.js
sonar-server/src/main/webapp/javascripts/select-list.js [deleted file]

index 229d1dda198338ea9b1e6e6cef1f9993c30e4866..6524fa5401acac580aae87f3a36a19b59739a8dd 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <div>
index 9b868e49a89e84dd574e93ad539d949bc9916d69..25ddfa3d24fc2abd4a4c9c19259fe59ed12056f6 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/issues.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/issues/app.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <div class="navigator">
index 48b96e38997b3c60d160220dff2ce91703263dba..0799536e3921b037dcec062dbc0b634da24e3bc7 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/measures.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/measures/app.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 
index f3a0524a5faab490446eaee6fb8ce44f1c075489..6522ec2d6b85bbee50b1c8b7e0f4144a8c303733 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <h1 class="admin-page-title"><%= message 'roles.page' -%></h1>
index 80a0584a1a71c96c62eb857ee21a64b7f5291a81..2e6435d6adda434e98065fcdf739156c81c556c7 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <div>
index 13dc30dbba9b293c373894e956a39a882496f334..9a2e6d5469e9d0fb8e8d46a2490b4e659fe51819 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <h1 class="admin-page-title"><%= h message 'global_permissions.page' -%></h1>
index bea1301a649c0c7db39f7601392607b86b042afa..8b80528ebdaa41fae9fc37b3e20040fc06b613f7 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <h1 class="admin-page-title"><%= message 'roles.page' -%></h1>
index 382c7909fdf1aa6e7e3007f0116b05284e5e9780..061c2fa33ba9fab188a3b1bddf00793ba1fd19c8 100644 (file)
@@ -1,5 +1,5 @@
 <% content_for :script do %>
-  <script data-main="<%= ApplicationController.root_context -%>/javascripts/select-list.js" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
+  <script data-main="<%= ApplicationController.root_context -%>/javascripts/common/select-list" src="<%= ApplicationController.root_context -%>/javascripts/third-party/require.js"></script>
 <% end %>
 
 <div>
index a249512080562b1389af6c1da5cffb02a8fa86c1..10215914eedbbbf26fbd973144209b21b1b52fcc 100644 (file)
@@ -5,8 +5,9 @@
 
   modules: [
        { name: 'quality-gate/app' },
-       { name: 'issues' },
-       { name: 'measures' }
+       { name: 'issues/app' },
+       { name: 'measures/app' },
+       { name: 'common/select-list' }
   ],
 
   paths: {
@@ -14,7 +15,7 @@
     'backbone.marionette': 'third-party/backbone.marionette',
     'handlebars': 'third-party/handlebars',
     'moment': 'third-party/moment',
-    'select-list': 'select-list'
+    'select-list': 'common/select-list'
   },
 
   shim: {
diff --git a/sonar-server/src/main/webapp/javascripts/common/handlebars-extensions.js b/sonar-server/src/main/webapp/javascripts/common/handlebars-extensions.js
new file mode 100644 (file)
index 0000000..b0cda32
--- /dev/null
@@ -0,0 +1,154 @@
+define(['handlebars'], function (Handlebars) {
+
+  var defaultActions = ['comment', 'assign', 'assign_to_me', 'plan', 'set_severity'];
+
+  Handlebars.registerHelper('capitalize', function(string) {
+    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+  });
+
+  Handlebars.registerHelper('severityIcon', function(severity) {
+    return new Handlebars.SafeString(
+        '<i class="icon-severity-' + severity.toLowerCase() + '"></i>'
+    );
+  });
+
+  Handlebars.registerHelper('statusIcon', function(status) {
+    return new Handlebars.SafeString(
+        '<i class="icon-status-' + status.toLowerCase() + '"></i>'
+    );
+  });
+
+  Handlebars.registerHelper('resolutionIcon', function(resolution) {
+    return new Handlebars.SafeString(
+        '<i class="icon-resolution-' + resolution.toLowerCase() + '"></i>'
+    );
+  });
+
+  Handlebars.registerHelper('eq', function(v1, v2, options) {
+    return v1 == v2 ? options.fn(this) : options.inverse(this);
+  });
+
+  Handlebars.registerHelper('notEq', function(v1, v2, options) {
+    return v1 != v2 ? options.fn(this) : options.inverse(this);
+  });
+
+  Handlebars.registerHelper('all', function() {
+    var args = Array.prototype.slice.call(arguments, 0, -1),
+        options = arguments[arguments.length - 1],
+        all = args.reduce(function(prev, current) {
+          return prev && current;
+        }, true);
+    return all ? options.fn(this) : options.inverse(this);
+  });
+
+  Handlebars.registerHelper('any', function() {
+    var args = Array.prototype.slice.call(arguments, 0, -1),
+        options = arguments[arguments.length - 1],
+        all = args.reduce(function(prev, current) {
+          return prev || current;
+        }, true);
+    return all ? options.fn(this) : options.inverse(this);
+  });
+
+  Handlebars.registerHelper('inArray', function(array, element, options) {
+    if (array.indexOf(element) !== -1) {
+      return options.fn(this);
+    } else {
+      return options.inverse(this);
+    }
+  });
+
+  Handlebars.registerHelper('ifNotEmpty', function() {
+    var args = Array.prototype.slice.call(arguments, 0, -1),
+        options = arguments[arguments.length - 1],
+        notEmpty = args.reduce(function(prev, current) {
+          return prev || (current && current.length > 0);
+        }, false);
+    return notEmpty ? options.fn(this) : '';
+  });
+
+  Handlebars.registerHelper('dashboardUrl', function(componentKey, componentQualifier) {
+    var url = '/dashboard/index/' + decodeURIComponent(componentKey);
+    if (componentQualifier === 'FIL' || componentQualifier === 'CLA') {
+      url += '?metric=sqale_index';
+    }
+    return url;
+  });
+
+  Handlebars.registerHelper('translate', function(key, prefix) {
+    var args = Array.prototype.slice.call(arguments, 0, -1),
+        tokens = args.reduce(function(prev, current) {
+          return prev.concat(current.split('.'));
+        }, []),
+        start = window.SS.phrases;
+
+    return tokens.reduce(function(prev, current) {
+      return current ? prev[current] : prev;
+    }, start);
+  });
+
+  Handlebars.registerHelper('pluginActions', function(actions, options) {
+    var pluginActions = _.difference(actions, defaultActions);
+    return pluginActions.reduce(function(prev, current) {
+      return prev + options.fn(current);
+    }, '');
+  });
+
+  Handlebars.registerHelper('ifHasExtraTransitions', function(transitions, options) {
+    if (transitions && transitions.length > 1) {
+      return options.fn(this);
+    } else {
+      return '';
+    }
+  });
+
+  Handlebars.registerHelper('ifHasExtraActions', function(actions, options) {
+    var actionsLeft = _.difference(actions, _.without(defaultActions, 'set_severity'));
+    if (actionsLeft.length > 0) {
+      return options.fn(this);
+    } else {
+      return '';
+    }
+  });
+
+  Handlebars.registerHelper('withFirst', function(list, options) {
+    if (list && list.length > 0) {
+      return options.fn(list[0]);
+    } else {
+      return '';
+    }
+  });
+
+  Handlebars.registerHelper('withoutFirst', function(list, options) {
+    if (list && list.length > 1) {
+      return list.slice(1).reduce(function(prev, current) {
+        return prev + options.fn(current);
+      }, '');
+    } else {
+      return '';
+    }
+  });
+
+  Handlebars.registerHelper('sources', function(source, scm, options) {
+    var sources = _.map(source, function(code, line) {
+      return {
+        lineNumber: line,
+        code: code,
+        scm: (scm && scm[line]) ? { author: scm[line][0], date: scm[line][1] } : undefined
+      }
+    });
+
+    return sources.reduce(function(prev, current, index) {
+      return prev + options.fn(_.extend({ first: index === 0 }, current));
+    }, '');
+  });
+
+  Handlebars.registerHelper('operators', function(metricType, options) {
+    var ops = ['LT', 'GT', 'EQ', 'NE'];
+
+    return ops.reduce(function(prev, current) {
+      return prev + options.fn(current);
+    }, '');
+  });
+
+});
diff --git a/sonar-server/src/main/webapp/javascripts/common/select-list.js b/sonar-server/src/main/webapp/javascripts/common/select-list.js
new file mode 100644 (file)
index 0000000..2f63fd8
--- /dev/null
@@ -0,0 +1,402 @@
+requirejs.config({
+  baseUrl: baseUrl + '/javascripts',
+
+  paths: {
+    'backbone': 'third-party/backbone'
+  },
+
+  shim: {
+    'backbone': {
+      exports: 'Backbone'
+    }
+  }
+
+});
+
+requirejs(['backbone'], function (Backbone) {
+
+  (function ($) {
+
+    var showError = null;
+
+    /*
+     * SelectList Collection
+     */
+
+    var SelectListCollection = Backbone.Collection.extend({
+
+      parse: function (r) {
+        this.more = r.more;
+        return r.results;
+      },
+
+      fetch: function (options) {
+        var data = $.extend({
+              page: 1,
+              pageSize: 100
+            }, options.data || {}),
+            settings = $.extend({}, options, { data: data });
+
+        this.settings = {
+          url: settings.url,
+          data: data
+        };
+
+        Backbone.Collection.prototype.fetch.call(this, settings);
+      },
+
+      fetchNextPage: function (options) {
+        if (this.more) {
+          var nextPage = this.settings.data.page + 1,
+              settings = $.extend(this.settings, options);
+
+          settings.data.page = nextPage;
+
+          this.fetch(settings);
+        }
+      }
+
+    });
+
+
+    /*
+     * SelectList Item View
+     */
+
+    var SelectListItemView = Backbone.View.extend({
+      tagName: 'li',
+
+      template: function (d) {
+        return  '<input class="select-list-list-checkbox" type="checkbox">' +
+            '<div class="select-list-list-item">' + d + '</div>';
+      },
+
+      events: {
+        'change .select-list-list-checkbox': 'toggle'
+      },
+
+      initialize: function (options) {
+        this.listenTo(this.model, 'change', this.render);
+        this.settings = options.settings;
+      },
+
+      render: function () {
+        this.$el.html(this.template(this.settings.format(this.model.toJSON())));
+        this.$('input').prop('name', this.model.get('name'));
+        this.$el.toggleClass('selected', this.model.get('selected'));
+        this.$('.select-list-list-checkbox')
+            .prop('title',
+                this.model.get('selected') ?
+                    this.settings.tooltips.deselect :
+                    this.settings.tooltips.select)
+            .prop('checked', this.model.get('selected'));
+      },
+
+      remove: function (postpone) {
+        if (postpone) {
+          var that = this;
+          that.$el.addClass(this.model.get('selected') ? 'added' : 'removed');
+          setTimeout(function () {
+            Backbone.View.prototype.remove.call(that, arguments);
+          }, 500);
+        } else {
+          Backbone.View.prototype.remove.call(this, arguments);
+        }
+      },
+
+      toggle: function () {
+        var selected = this.model.get('selected'),
+            that = this,
+            url = selected ? this.settings.deselectUrl : this.settings.selectUrl,
+            data = $.extend({}, this.settings.extra || {});
+
+        data[this.settings.selectParameter] = this.model.get(this.settings.selectParameterValue);
+
+        that.$el.addClass('progress');
+        $.ajax({
+          url: url,
+          type: 'POST',
+          data: data
+        })
+            .done(function () {
+              that.model.set('selected', !selected);
+            })
+            .fail(showError)
+            .always(function () {
+              that.$el.removeClass('progress');
+            });
+      }
+    });
+
+
+    /*
+     * SelectList View
+     */
+
+    var SelectListView = Backbone.View.extend({
+      template: function (l) {
+        return '<div class="select-list-container">' +
+            '<div class="select-list-control">' +
+            '<div class="select-list-check-control">' +
+            '<a class="select-list-control-button" name="selected">' + l.selected + '</a>' +
+            '<a class="select-list-control-button" name="deselected">' + l.deselected + '</a>' +
+            '<a class="select-list-control-button" name="all">' + l.all + '</a>' +
+            '</div>' +
+            '<div class="select-list-search-control">' +
+            '<input type="text" placeholder="Search">' +
+            '<a class="select-list-search-control-clear">&times;</a>' +
+            '</div>' +
+            '</div>' +
+            '<div class="select-list-list-container">' +
+            '<ul class="select-list-list"></ul>' +
+            '</div>' +
+            '</div>';
+      },
+
+      events: {
+        'click .select-list-control-button[name=selected]': 'showSelected',
+        'click .select-list-control-button[name=deselected]': 'showDeselected',
+        'click .select-list-control-button[name=all]': 'showAll',
+
+        'click .select-list-search-control-clear': 'clearSearch'
+      },
+
+      initialize: function (options) {
+        this.listenTo(this.collection, 'add', this.renderListItem);
+        this.listenTo(this.collection, 'reset', this.renderList);
+        this.listenTo(this.collection, 'remove', this.removeModel);
+        this.listenTo(this.collection, 'change:selected', this.confirmFilter);
+        this.settings = options.settings;
+      },
+
+      render: function () {
+        var that = this,
+            keyup = function () {
+              that.search();
+            };
+
+        this.$el.html(this.template(this.settings.labels))
+            .width(this.settings.width);
+
+        this.$listContainer = this.$('.select-list-list-container')
+            .height(this.settings.height)
+            .css('overflow', 'auto')
+            .on('scroll', function () {
+              that.scroll();
+            });
+
+        this.$list = this.$('.select-list-list');
+
+        var searchInput = this.$('.select-list-search-control input')
+            .on('keyup', $.debounce(250, keyup));
+
+        setTimeout(function () {
+          searchInput.focus();
+        }, 250);
+
+        this.listItemViews = [];
+
+        showError = function () {
+          $('<div>')
+              .addClass('error').text(that.settings.errorMessage)
+              .insertBefore(that.$el);
+        };
+      },
+
+      renderList: function () {
+        this.listItemViews.forEach(function (view) {
+          view.remove();
+        });
+        this.listItemViews = [];
+        this.collection.each(this.renderListItem, this);
+        this.$listContainer.scrollTop(0);
+      },
+
+      renderListItem: function (item) {
+        var itemView = new SelectListItemView({
+          model: item,
+          settings: this.settings
+        });
+        this.listItemViews.push(itemView);
+        this.$list.append(itemView.el);
+        itemView.render();
+      },
+
+      confirmFilter: function (model) {
+        if (this.currentFilter !== 'all') {
+          this.collection.remove(model);
+        }
+      },
+
+      removeModel: function (model, collection, options) {
+        this.listItemViews[options.index].remove(true);
+        this.listItemViews.splice(options.index, 1);
+      },
+
+      filterBySelection: function (filter) {
+        var that = this;
+        filter = this.currentFilter = filter || this.currentFilter;
+
+        if (filter != null) {
+          this.$('.select-list-check-control').toggleClass('disabled', false);
+          this.$('.select-list-search-control').toggleClass('disabled', true);
+          this.$('.select-list-search-control input').val('');
+
+          this.$('.select-list-control-button').removeClass('active')
+              .filter('[name=' + filter + ']').addClass('active');
+
+          this.showFetchSpinner();
+
+          this.collection.fetch({
+            url: this.settings.searchUrl,
+            reset: true,
+            data: { selected: filter },
+            success: function () {
+              that.hideFetchSpinner();
+            },
+            error: showError
+          });
+        }
+      },
+
+      showSelected: function () {
+        this.filterBySelection('selected');
+      },
+
+      showDeselected: function () {
+        this.filterBySelection('deselected');
+      },
+
+      showAll: function () {
+        this.filterBySelection('all');
+      },
+
+      search: function () {
+        var query = this.$('.select-list-search-control input').val(),
+            hasQuery = query.length > 0,
+            that = this;
+
+        this.$('.select-list-check-control').toggleClass('disabled', hasQuery);
+        this.$('.select-list-search-control').toggleClass('disabled', !hasQuery);
+
+        if (hasQuery) {
+          this.showFetchSpinner();
+          this.currentFilter = 'all';
+
+          this.collection.fetch({
+            url: this.settings.searchUrl,
+            reset: true,
+            data: { query: query },
+            success: function () {
+              that.hideFetchSpinner();
+            },
+            error: showError
+          });
+        } else {
+          this.filterBySelection();
+        }
+      },
+
+      searchByQuery: function (query) {
+        this.$('.select-list-search-control input').val(query);
+        this.search();
+      },
+
+      clearSearch: function () {
+        this.filterBySelection();
+      },
+
+      showFetchSpinner: function () {
+        this.$listContainer.addClass('loading');
+      },
+
+      hideFetchSpinner: function () {
+        this.$listContainer.removeClass('loading');
+      },
+
+      scroll: function () {
+        var scrollBottom = this.$listContainer.scrollTop() >=
+                this.$list[0].scrollHeight - this.$listContainer.outerHeight(),
+            that = this;
+
+        if (scrollBottom && this.collection.more) {
+          $.throttle(250, function () {
+            that.showFetchSpinner();
+
+            that.collection.fetchNextPage({
+              success: function () {
+                that.hideFetchSpinner();
+              }
+            });
+          })();
+        }
+      }
+
+    });
+
+
+    /*
+     * SelectList Entry Point
+     */
+
+    window.SelectList = function (options) {
+      this.settings = $.extend(window.SelectList.defaults, options);
+
+      this.collection = new SelectListCollection();
+
+      this.view = new SelectListView({
+        el: this.settings.el,
+        collection: this.collection,
+        settings: this.settings
+      });
+
+      this.view.render();
+      this.filter('selected');
+      return this;
+    };
+
+
+    /*
+     * SelectList API Methods
+     */
+
+    window.SelectList.prototype.filter = function (filter) {
+      this.view.filterBySelection(filter);
+      return this;
+    };
+
+    window.SelectList.prototype.search = function (query) {
+      this.view.searchByQuery(query);
+      return this;
+    };
+
+
+    /*
+     * SelectList Defaults
+     */
+
+    window.SelectList.defaults = {
+      width: '50%',
+      height: 400,
+
+      format: function (item) {
+        return item.value;
+      },
+
+      labels: {
+        selected: 'Selected',
+        deselected: 'Deselected',
+        all: 'All'
+      },
+
+      tooltips: {
+        select: 'Click this to select item',
+        deselect: 'Click this to deselect item'
+      },
+
+      errorMessage: 'Something gone wrong, try to reload the page and try again.'
+    };
+
+  })(jQuery);
+
+});
diff --git a/sonar-server/src/main/webapp/javascripts/handlebars-extensions.js b/sonar-server/src/main/webapp/javascripts/handlebars-extensions.js
deleted file mode 100644 (file)
index b0cda32..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-define(['handlebars'], function (Handlebars) {
-
-  var defaultActions = ['comment', 'assign', 'assign_to_me', 'plan', 'set_severity'];
-
-  Handlebars.registerHelper('capitalize', function(string) {
-    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
-  });
-
-  Handlebars.registerHelper('severityIcon', function(severity) {
-    return new Handlebars.SafeString(
-        '<i class="icon-severity-' + severity.toLowerCase() + '"></i>'
-    );
-  });
-
-  Handlebars.registerHelper('statusIcon', function(status) {
-    return new Handlebars.SafeString(
-        '<i class="icon-status-' + status.toLowerCase() + '"></i>'
-    );
-  });
-
-  Handlebars.registerHelper('resolutionIcon', function(resolution) {
-    return new Handlebars.SafeString(
-        '<i class="icon-resolution-' + resolution.toLowerCase() + '"></i>'
-    );
-  });
-
-  Handlebars.registerHelper('eq', function(v1, v2, options) {
-    return v1 == v2 ? options.fn(this) : options.inverse(this);
-  });
-
-  Handlebars.registerHelper('notEq', function(v1, v2, options) {
-    return v1 != v2 ? options.fn(this) : options.inverse(this);
-  });
-
-  Handlebars.registerHelper('all', function() {
-    var args = Array.prototype.slice.call(arguments, 0, -1),
-        options = arguments[arguments.length - 1],
-        all = args.reduce(function(prev, current) {
-          return prev && current;
-        }, true);
-    return all ? options.fn(this) : options.inverse(this);
-  });
-
-  Handlebars.registerHelper('any', function() {
-    var args = Array.prototype.slice.call(arguments, 0, -1),
-        options = arguments[arguments.length - 1],
-        all = args.reduce(function(prev, current) {
-          return prev || current;
-        }, true);
-    return all ? options.fn(this) : options.inverse(this);
-  });
-
-  Handlebars.registerHelper('inArray', function(array, element, options) {
-    if (array.indexOf(element) !== -1) {
-      return options.fn(this);
-    } else {
-      return options.inverse(this);
-    }
-  });
-
-  Handlebars.registerHelper('ifNotEmpty', function() {
-    var args = Array.prototype.slice.call(arguments, 0, -1),
-        options = arguments[arguments.length - 1],
-        notEmpty = args.reduce(function(prev, current) {
-          return prev || (current && current.length > 0);
-        }, false);
-    return notEmpty ? options.fn(this) : '';
-  });
-
-  Handlebars.registerHelper('dashboardUrl', function(componentKey, componentQualifier) {
-    var url = '/dashboard/index/' + decodeURIComponent(componentKey);
-    if (componentQualifier === 'FIL' || componentQualifier === 'CLA') {
-      url += '?metric=sqale_index';
-    }
-    return url;
-  });
-
-  Handlebars.registerHelper('translate', function(key, prefix) {
-    var args = Array.prototype.slice.call(arguments, 0, -1),
-        tokens = args.reduce(function(prev, current) {
-          return prev.concat(current.split('.'));
-        }, []),
-        start = window.SS.phrases;
-
-    return tokens.reduce(function(prev, current) {
-      return current ? prev[current] : prev;
-    }, start);
-  });
-
-  Handlebars.registerHelper('pluginActions', function(actions, options) {
-    var pluginActions = _.difference(actions, defaultActions);
-    return pluginActions.reduce(function(prev, current) {
-      return prev + options.fn(current);
-    }, '');
-  });
-
-  Handlebars.registerHelper('ifHasExtraTransitions', function(transitions, options) {
-    if (transitions && transitions.length > 1) {
-      return options.fn(this);
-    } else {
-      return '';
-    }
-  });
-
-  Handlebars.registerHelper('ifHasExtraActions', function(actions, options) {
-    var actionsLeft = _.difference(actions, _.without(defaultActions, 'set_severity'));
-    if (actionsLeft.length > 0) {
-      return options.fn(this);
-    } else {
-      return '';
-    }
-  });
-
-  Handlebars.registerHelper('withFirst', function(list, options) {
-    if (list && list.length > 0) {
-      return options.fn(list[0]);
-    } else {
-      return '';
-    }
-  });
-
-  Handlebars.registerHelper('withoutFirst', function(list, options) {
-    if (list && list.length > 1) {
-      return list.slice(1).reduce(function(prev, current) {
-        return prev + options.fn(current);
-      }, '');
-    } else {
-      return '';
-    }
-  });
-
-  Handlebars.registerHelper('sources', function(source, scm, options) {
-    var sources = _.map(source, function(code, line) {
-      return {
-        lineNumber: line,
-        code: code,
-        scm: (scm && scm[line]) ? { author: scm[line][0], date: scm[line][1] } : undefined
-      }
-    });
-
-    return sources.reduce(function(prev, current, index) {
-      return prev + options.fn(_.extend({ first: index === 0 }, current));
-    }, '');
-  });
-
-  Handlebars.registerHelper('operators', function(metricType, options) {
-    var ops = ['LT', 'GT', 'EQ', 'NE'];
-
-    return ops.reduce(function(prev, current) {
-      return prev + options.fn(current);
-    }, '');
-  });
-
-});
diff --git a/sonar-server/src/main/webapp/javascripts/issues-extra.js b/sonar-server/src/main/webapp/javascripts/issues-extra.js
deleted file mode 100644 (file)
index 82e5ac5..0000000
+++ /dev/null
@@ -1,1240 +0,0 @@
-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: {
-          canManageFilter: false,
-          canBulkChange: false
-        },
-
-
-        url: function () {
-          return baseUrl + '/api/issue_filters/page';
-        }
-
-      });
-
-
-      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({
-        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.render();
-            return;
-          }
-        },
-
-
-        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 = this.collection
-              .where({ enabled: false })
-              .reject(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()
-          }, 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: this.model.get('choices')
-          });
-        }
-      });
-
-
-      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
-      };
-
-    });
diff --git a/sonar-server/src/main/webapp/javascripts/issues.js b/sonar-server/src/main/webapp/javascripts/issues.js
deleted file mode 100644 (file)
index e1c20eb..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-requirejs.config({
-
-  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',
-
-      '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'
-      });
-
-
-      NavigatorApp.addInitializer(function () {
-        jQuery('html').addClass('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/webapp/javascripts/issues/app.js b/sonar-server/src/main/webapp/javascripts/issues/app.js
new file mode 100644 (file)
index 0000000..98faddb
--- /dev/null
@@ -0,0 +1,399 @@
+requirejs.config({
+  baseUrl: baseUrl + '/javascripts',
+
+  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'
+      });
+
+
+      NavigatorApp.addInitializer(function () {
+        jQuery('html').addClass('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/webapp/javascripts/issues/extra.js b/sonar-server/src/main/webapp/javascripts/issues/extra.js
new file mode 100644 (file)
index 0000000..4432426
--- /dev/null
@@ -0,0 +1,1240 @@
+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: {
+          canManageFilter: false,
+          canBulkChange: false
+        },
+
+
+        url: function () {
+          return baseUrl + '/api/issue_filters/page';
+        }
+
+      });
+
+
+      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({
+        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.render();
+            return;
+          }
+        },
+
+
+        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 = this.collection
+              .where({ enabled: false })
+              .reject(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()
+          }, 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: this.model.get('choices')
+          });
+        }
+      });
+
+
+      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
+      };
+
+    });
diff --git a/sonar-server/src/main/webapp/javascripts/measures.js b/sonar-server/src/main/webapp/javascripts/measures.js
deleted file mode 100644 (file)
index 2314f18..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-requirejs.config({
-
-  paths: {
-    'backbone': 'third-party/backbone',
-    'backbone.marionette': 'third-party/backbone.marionette',
-    'handlebars': 'third-party/handlebars'
-  },
-
-  shim: {
-    'backbone.marionette': {
-      deps: ['backbone'],
-      exports: 'Marionette'
-    },
-    'backbone': {
-      exports: 'Backbone'
-    },
-    'handlebars': {
-      exports: 'Handlebars'
-    }
-  }
-
-});
-
-requirejs(
-    [
-      'backbone', 'backbone.marionette',
-      '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/string-filters',
-      'navigator/filters/metric-filters'
-    ],
-    function (Backbone, Marionette, FilterBar, BaseFilters, CheckboxFilterView, ChoiceFilters, AjaxSelectFilters,
-              FavoriteFilters, RangeFilters, StringFilterView, MetricFilterView) {
-
-      _.templateSettings = {
-        interpolate: /\{\{(.+?)\}\}/g,
-        evaluate: /\{\[(.+?)\]\}/g,
-        escape: /\{\{\{(.+?)\}\}\}/g
-      };
-
-      var NavigatorApp = new Marionette.Application();
-
-
-      NavigatorApp.addRegions({
-        filtersRegion: '.navigator-filters'
-      });
-
-
-      NavigatorApp.addInitializer(function () {
-        this.filters = new BaseFilters.Filters();
-
-        if (_.isObject(window.SS.favorites)) {
-          this.filters.add([
-            new BaseFilters.Filter({
-              type: FavoriteFilters.FavoriteFilterView,
-              enabled: true,
-              optional: false,
-              choices: window.SS.favorites,
-              favoriteUrl: '/measures/filter',
-              manageUrl: '/measures/manage'
-            })]);
-        }
-
-        this.filters.add([
-          new BaseFilters.Filter({
-            name: window.SS.phrases.components,
-            property: 'qualifiers[]',
-            type: ChoiceFilters.ChoiceFilterView,
-            enabled: true,
-            optional: false,
-            choices: window.SS.qualifiers,
-            defaultValue: window.SS.phrases.any
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.alert,
-            property: 'alertLevels[]',
-            type: ChoiceFilters.ChoiceFilterView,
-            enabled: false,
-            optional: true,
-            choices: {
-              'error': window.SS.phrases.error,
-              'warn': window.SS.phrases.warning,
-              'ok': window.SS.phrases.ok
-            }
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.componentsOf,
-            property: 'base',
-            type: AjaxSelectFilters.ComponentFilterView,
-            multiple: false,
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.favoritesOnly,
-            property: 'onFavourites',
-            type: CheckboxFilterView,
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.date,
-            propertyFrom: 'fromDate',
-            propertyTo: 'toDate',
-            type: RangeFilters.DateRangeFilterView,
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.keyContains,
-            property: 'keySearch',
-            type: StringFilterView,
-            enabled: false,
-            optional: true
-          })
-        ]);
-
-        if (_.isObject(window.SS.languages) && _.size(window.SS.languages) > 1) {
-          this.filters.add([
-            new BaseFilters.Filter({
-              name: window.SS.phrases.language,
-              property: 'languages[]',
-              type: ChoiceFilters.ChoiceFilterView,
-              enabled: false,
-              optional: true,
-              choices: window.SS.languages
-            })
-          ]);
-        }
-
-        this.filters.add([
-          new BaseFilters.Filter({
-            name: window.SS.phrases.lastAnalysis,
-            propertyFrom: 'ageMinDays',
-            propertyTo: 'ageMaxDays',
-            type: RangeFilters.RangeFilterView,
-            placeholder: window.SS.phrases.days,
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.metric,
-            property: 'c3',
-            type: MetricFilterView,
-            metrics: window.SS.metrics,
-            periods: window.SS.metricPeriods,
-            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.metric,
-            property: 'c2',
-            type: MetricFilterView,
-            metrics: window.SS.metrics,
-            periods: window.SS.metricPeriods,
-            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.metric,
-            property: 'c1',
-            type: MetricFilterView,
-            metrics: window.SS.metrics,
-            periods: window.SS.metricPeriods,
-            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
-            enabled: false,
-            optional: true
-          }),
-
-          new BaseFilters.Filter({
-            name: window.SS.phrases.nameContains,
-            property: 'nameSearch',
-            type: StringFilterView,
-            enabled: false,
-            optional: true
-          })
-        ]);
-
-
-        this.filterBarView = new FilterBar({
-          collection: this.filters,
-          extra: {
-            sort: '',
-            asc: false
-          }
-        });
-
-
-        this.filtersRegion.show(this.filterBarView);
-      });
-
-
-      NavigatorApp.start();
-      if (window.queryParams) {
-        NavigatorApp.filterBarView.restoreFromQuery(window.queryParams);
-      }
-
-    });
diff --git a/sonar-server/src/main/webapp/javascripts/measures/app.js b/sonar-server/src/main/webapp/javascripts/measures/app.js
new file mode 100644 (file)
index 0000000..ac2781a
--- /dev/null
@@ -0,0 +1,214 @@
+requirejs.config({
+  baseUrl: baseUrl + '/javascripts',
+
+  paths: {
+    'backbone': 'third-party/backbone',
+    'backbone.marionette': 'third-party/backbone.marionette',
+    'handlebars': 'third-party/handlebars'
+  },
+
+  shim: {
+    'backbone.marionette': {
+      deps: ['backbone'],
+      exports: 'Marionette'
+    },
+    'backbone': {
+      exports: 'Backbone'
+    },
+    'handlebars': {
+      exports: 'Handlebars'
+    }
+  }
+
+});
+
+requirejs(
+    [
+      'backbone', 'backbone.marionette',
+      '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/string-filters',
+      'navigator/filters/metric-filters'
+    ],
+    function (Backbone, Marionette, FilterBar, BaseFilters, CheckboxFilterView, ChoiceFilters, AjaxSelectFilters,
+              FavoriteFilters, RangeFilters, StringFilterView, MetricFilterView) {
+
+      _.templateSettings = {
+        interpolate: /\{\{(.+?)\}\}/g,
+        evaluate: /\{\[(.+?)\]\}/g,
+        escape: /\{\{\{(.+?)\}\}\}/g
+      };
+
+      var NavigatorApp = new Marionette.Application();
+
+
+      NavigatorApp.addRegions({
+        filtersRegion: '.navigator-filters'
+      });
+
+
+      NavigatorApp.addInitializer(function () {
+        this.filters = new BaseFilters.Filters();
+
+        if (_.isObject(window.SS.favorites)) {
+          this.filters.add([
+            new BaseFilters.Filter({
+              type: FavoriteFilters.FavoriteFilterView,
+              enabled: true,
+              optional: false,
+              choices: window.SS.favorites,
+              favoriteUrl: '/measures/filter',
+              manageUrl: '/measures/manage'
+            })]);
+        }
+
+        this.filters.add([
+          new BaseFilters.Filter({
+            name: window.SS.phrases.components,
+            property: 'qualifiers[]',
+            type: ChoiceFilters.ChoiceFilterView,
+            enabled: true,
+            optional: false,
+            choices: window.SS.qualifiers,
+            defaultValue: window.SS.phrases.any
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.alert,
+            property: 'alertLevels[]',
+            type: ChoiceFilters.ChoiceFilterView,
+            enabled: false,
+            optional: true,
+            choices: {
+              'error': window.SS.phrases.error,
+              'warn': window.SS.phrases.warning,
+              'ok': window.SS.phrases.ok
+            }
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.componentsOf,
+            property: 'base',
+            type: AjaxSelectFilters.ComponentFilterView,
+            multiple: false,
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.favoritesOnly,
+            property: 'onFavourites',
+            type: CheckboxFilterView,
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.date,
+            propertyFrom: 'fromDate',
+            propertyTo: 'toDate',
+            type: RangeFilters.DateRangeFilterView,
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.keyContains,
+            property: 'keySearch',
+            type: StringFilterView,
+            enabled: false,
+            optional: true
+          })
+        ]);
+
+        if (_.isObject(window.SS.languages) && _.size(window.SS.languages) > 1) {
+          this.filters.add([
+            new BaseFilters.Filter({
+              name: window.SS.phrases.language,
+              property: 'languages[]',
+              type: ChoiceFilters.ChoiceFilterView,
+              enabled: false,
+              optional: true,
+              choices: window.SS.languages
+            })
+          ]);
+        }
+
+        this.filters.add([
+          new BaseFilters.Filter({
+            name: window.SS.phrases.lastAnalysis,
+            propertyFrom: 'ageMinDays',
+            propertyTo: 'ageMaxDays',
+            type: RangeFilters.RangeFilterView,
+            placeholder: window.SS.phrases.days,
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.metric,
+            property: 'c3',
+            type: MetricFilterView,
+            metrics: window.SS.metrics,
+            periods: window.SS.metricPeriods,
+            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.metric,
+            property: 'c2',
+            type: MetricFilterView,
+            metrics: window.SS.metrics,
+            periods: window.SS.metricPeriods,
+            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.metric,
+            property: 'c1',
+            type: MetricFilterView,
+            metrics: window.SS.metrics,
+            periods: window.SS.metricPeriods,
+            operations: { 'eq': '=', 'lt': '<', 'lte': '≤', 'gt': '>', 'gte': '≥' },
+            enabled: false,
+            optional: true
+          }),
+
+          new BaseFilters.Filter({
+            name: window.SS.phrases.nameContains,
+            property: 'nameSearch',
+            type: StringFilterView,
+            enabled: false,
+            optional: true
+          })
+        ]);
+
+
+        this.filterBarView = new FilterBar({
+          collection: this.filters,
+          extra: {
+            sort: '',
+            asc: false
+          }
+        });
+
+
+        this.filtersRegion.show(this.filterBarView);
+      });
+
+
+      NavigatorApp.start();
+      if (window.queryParams) {
+        NavigatorApp.filterBarView.restoreFromQuery(window.queryParams);
+      }
+
+    });
diff --git a/sonar-server/src/main/webapp/javascripts/quality-gate-build-config.js b/sonar-server/src/main/webapp/javascripts/quality-gate-build-config.js
deleted file mode 100644 (file)
index ff40816..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-({
-  baseUrl: 'quality-gate',
-  paths: {
-    'backbone': '../third-party/backbone',
-    'backbone.marionette': '../third-party/backbone.marionette',
-    'handlebars': '../third-party/handlebars',
-    'moment': '../third-party/moment',
-    'select-list': '../select-list'
-  },
-  name: 'app',
-  out: 'app.build.js'
-})
index b40dd599f915aebaea09638604d46f0304143d93..bc434b0fee9635cf9cd504cefef61a629d91f8b8 100644 (file)
@@ -1,12 +1,12 @@
 requirejs.config
-  baseUrl: 'javascripts'
+  baseUrl: "#{baseUrl}/javascripts"
 
   paths:
     'backbone': 'third-party/backbone'
     'backbone.marionette': 'third-party/backbone.marionette'
     'handlebars': 'third-party/handlebars'
     'moment': 'third-party/moment'
-    'select-list': 'select-list'
+    'select-list': 'common/select-list'
 
   shim:
     'backbone.marionette':
@@ -28,7 +28,7 @@ requirejs [
   'quality-gate/collections/metrics',
   'quality-gate/views/quality-gate-sidebar-list-view',
   'quality-gate/router'
-  'handlebars-extensions'
+  'common/handlebars-extensions'
 ], (
   Backbone, Marionette, Handlebars,
   QualityGates,
index 51ec105c419f67f9b3e63e8c9a7746665b35cc0f..0267a75dce9688c0e570f167006fd06575659aa2 100644 (file)
@@ -1,13 +1,13 @@
 // Generated by CoffeeScript 1.6.3
 (function() {
   requirejs.config({
-    baseUrl: 'javascripts',
+    baseUrl: "" + baseUrl + "/javascripts",
     paths: {
       'backbone': 'third-party/backbone',
       'backbone.marionette': 'third-party/backbone.marionette',
       'handlebars': 'third-party/handlebars',
       'moment': 'third-party/moment',
-      'select-list': 'select-list'
+      'select-list': 'common/select-list'
     },
     shim: {
       'backbone.marionette': {
@@ -29,7 +29,7 @@
     }
   });
 
-  requirejs(['backbone', 'backbone.marionette', 'handlebars', 'quality-gate/collections/quality-gates', 'quality-gate/collections/metrics', 'quality-gate/views/quality-gate-sidebar-list-view', 'quality-gate/router', 'handlebars-extensions'], function(Backbone, Marionette, Handlebars, QualityGates, Metrics, QualityGateSidebarListItemView, QualityGateRouter) {
+  requirejs(['backbone', 'backbone.marionette', 'handlebars', 'quality-gate/collections/quality-gates', 'quality-gate/collections/metrics', 'quality-gate/views/quality-gate-sidebar-list-view', 'quality-gate/router', 'common/handlebars-extensions'], function(Backbone, Marionette, Handlebars, QualityGates, Metrics, QualityGateSidebarListItemView, QualityGateRouter) {
     var App;
     jQuery.ajaxSetup({
       error: function(jqXHR) {
diff --git a/sonar-server/src/main/webapp/javascripts/select-list.js b/sonar-server/src/main/webapp/javascripts/select-list.js
deleted file mode 100644 (file)
index 0a3bfef..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-requirejs.config({
-
-  paths: {
-    'backbone': 'third-party/backbone'
-  },
-
-  shim: {
-    'backbone': {
-      exports: 'Backbone'
-    }
-  }
-
-});
-
-requirejs(['backbone'], function (Backbone) {
-
-  (function ($) {
-
-    var showError = null;
-
-    /*
-     * SelectList Collection
-     */
-
-    var SelectListCollection = Backbone.Collection.extend({
-
-      parse: function (r) {
-        this.more = r.more;
-        return r.results;
-      },
-
-      fetch: function (options) {
-        var data = $.extend({
-              page: 1,
-              pageSize: 100
-            }, options.data || {}),
-            settings = $.extend({}, options, { data: data });
-
-        this.settings = {
-          url: settings.url,
-          data: data
-        };
-
-        Backbone.Collection.prototype.fetch.call(this, settings);
-      },
-
-      fetchNextPage: function (options) {
-        if (this.more) {
-          var nextPage = this.settings.data.page + 1,
-              settings = $.extend(this.settings, options);
-
-          settings.data.page = nextPage;
-
-          this.fetch(settings);
-        }
-      }
-
-    });
-
-
-    /*
-     * SelectList Item View
-     */
-
-    var SelectListItemView = Backbone.View.extend({
-      tagName: 'li',
-
-      template: function (d) {
-        return  '<input class="select-list-list-checkbox" type="checkbox">' +
-            '<div class="select-list-list-item">' + d + '</div>';
-      },
-
-      events: {
-        'change .select-list-list-checkbox': 'toggle'
-      },
-
-      initialize: function (options) {
-        this.listenTo(this.model, 'change', this.render);
-        this.settings = options.settings;
-      },
-
-      render: function () {
-        this.$el.html(this.template(this.settings.format(this.model.toJSON())));
-        this.$('input').prop('name', this.model.get('name'));
-        this.$el.toggleClass('selected', this.model.get('selected'));
-        this.$('.select-list-list-checkbox')
-            .prop('title',
-                this.model.get('selected') ?
-                    this.settings.tooltips.deselect :
-                    this.settings.tooltips.select)
-            .prop('checked', this.model.get('selected'));
-      },
-
-      remove: function (postpone) {
-        if (postpone) {
-          var that = this;
-          that.$el.addClass(this.model.get('selected') ? 'added' : 'removed');
-          setTimeout(function () {
-            Backbone.View.prototype.remove.call(that, arguments);
-          }, 500);
-        } else {
-          Backbone.View.prototype.remove.call(this, arguments);
-        }
-      },
-
-      toggle: function () {
-        var selected = this.model.get('selected'),
-            that = this,
-            url = selected ? this.settings.deselectUrl : this.settings.selectUrl,
-            data = $.extend({}, this.settings.extra || {});
-
-        data[this.settings.selectParameter] = this.model.get(this.settings.selectParameterValue);
-
-        that.$el.addClass('progress');
-        $.ajax({
-          url: url,
-          type: 'POST',
-          data: data
-        })
-            .done(function () {
-              that.model.set('selected', !selected);
-            })
-            .fail(showError)
-            .always(function () {
-              that.$el.removeClass('progress');
-            });
-      }
-    });
-
-
-    /*
-     * SelectList View
-     */
-
-    var SelectListView = Backbone.View.extend({
-      template: function (l) {
-        return '<div class="select-list-container">' +
-            '<div class="select-list-control">' +
-            '<div class="select-list-check-control">' +
-            '<a class="select-list-control-button" name="selected">' + l.selected + '</a>' +
-            '<a class="select-list-control-button" name="deselected">' + l.deselected + '</a>' +
-            '<a class="select-list-control-button" name="all">' + l.all + '</a>' +
-            '</div>' +
-            '<div class="select-list-search-control">' +
-            '<input type="text" placeholder="Search">' +
-            '<a class="select-list-search-control-clear">&times;</a>' +
-            '</div>' +
-            '</div>' +
-            '<div class="select-list-list-container">' +
-            '<ul class="select-list-list"></ul>' +
-            '</div>' +
-            '</div>';
-      },
-
-      events: {
-        'click .select-list-control-button[name=selected]': 'showSelected',
-        'click .select-list-control-button[name=deselected]': 'showDeselected',
-        'click .select-list-control-button[name=all]': 'showAll',
-
-        'click .select-list-search-control-clear': 'clearSearch'
-      },
-
-      initialize: function (options) {
-        this.listenTo(this.collection, 'add', this.renderListItem);
-        this.listenTo(this.collection, 'reset', this.renderList);
-        this.listenTo(this.collection, 'remove', this.removeModel);
-        this.listenTo(this.collection, 'change:selected', this.confirmFilter);
-        this.settings = options.settings;
-      },
-
-      render: function () {
-        var that = this,
-            keyup = function () {
-              that.search();
-            };
-
-        this.$el.html(this.template(this.settings.labels))
-            .width(this.settings.width);
-
-        this.$listContainer = this.$('.select-list-list-container')
-            .height(this.settings.height)
-            .css('overflow', 'auto')
-            .on('scroll', function () {
-              that.scroll();
-            });
-
-        this.$list = this.$('.select-list-list');
-
-        var searchInput = this.$('.select-list-search-control input')
-            .on('keyup', $.debounce(250, keyup));
-
-        setTimeout(function () {
-          searchInput.focus();
-        }, 250);
-
-        this.listItemViews = [];
-
-        showError = function () {
-          $('<div>')
-              .addClass('error').text(that.settings.errorMessage)
-              .insertBefore(that.$el);
-        };
-      },
-
-      renderList: function () {
-        this.listItemViews.forEach(function (view) {
-          view.remove();
-        });
-        this.listItemViews = [];
-        this.collection.each(this.renderListItem, this);
-        this.$listContainer.scrollTop(0);
-      },
-
-      renderListItem: function (item) {
-        var itemView = new SelectListItemView({
-          model: item,
-          settings: this.settings
-        });
-        this.listItemViews.push(itemView);
-        this.$list.append(itemView.el);
-        itemView.render();
-      },
-
-      confirmFilter: function (model) {
-        if (this.currentFilter !== 'all') {
-          this.collection.remove(model);
-        }
-      },
-
-      removeModel: function (model, collection, options) {
-        this.listItemViews[options.index].remove(true);
-        this.listItemViews.splice(options.index, 1);
-      },
-
-      filterBySelection: function (filter) {
-        var that = this;
-        filter = this.currentFilter = filter || this.currentFilter;
-
-        if (filter != null) {
-          this.$('.select-list-check-control').toggleClass('disabled', false);
-          this.$('.select-list-search-control').toggleClass('disabled', true);
-          this.$('.select-list-search-control input').val('');
-
-          this.$('.select-list-control-button').removeClass('active')
-              .filter('[name=' + filter + ']').addClass('active');
-
-          this.showFetchSpinner();
-
-          this.collection.fetch({
-            url: this.settings.searchUrl,
-            reset: true,
-            data: { selected: filter },
-            success: function () {
-              that.hideFetchSpinner();
-            },
-            error: showError
-          });
-        }
-      },
-
-      showSelected: function () {
-        this.filterBySelection('selected');
-      },
-
-      showDeselected: function () {
-        this.filterBySelection('deselected');
-      },
-
-      showAll: function () {
-        this.filterBySelection('all');
-      },
-
-      search: function () {
-        var query = this.$('.select-list-search-control input').val(),
-            hasQuery = query.length > 0,
-            that = this;
-
-        this.$('.select-list-check-control').toggleClass('disabled', hasQuery);
-        this.$('.select-list-search-control').toggleClass('disabled', !hasQuery);
-
-        if (hasQuery) {
-          this.showFetchSpinner();
-          this.currentFilter = 'all';
-
-          this.collection.fetch({
-            url: this.settings.searchUrl,
-            reset: true,
-            data: { query: query },
-            success: function () {
-              that.hideFetchSpinner();
-            },
-            error: showError
-          });
-        } else {
-          this.filterBySelection();
-        }
-      },
-
-      searchByQuery: function (query) {
-        this.$('.select-list-search-control input').val(query);
-        this.search();
-      },
-
-      clearSearch: function () {
-        this.filterBySelection();
-      },
-
-      showFetchSpinner: function () {
-        this.$listContainer.addClass('loading');
-      },
-
-      hideFetchSpinner: function () {
-        this.$listContainer.removeClass('loading');
-      },
-
-      scroll: function () {
-        var scrollBottom = this.$listContainer.scrollTop() >=
-                this.$list[0].scrollHeight - this.$listContainer.outerHeight(),
-            that = this;
-
-        if (scrollBottom && this.collection.more) {
-          $.throttle(250, function () {
-            that.showFetchSpinner();
-
-            that.collection.fetchNextPage({
-              success: function () {
-                that.hideFetchSpinner();
-              }
-            });
-          })();
-        }
-      }
-
-    });
-
-
-    /*
-     * SelectList Entry Point
-     */
-
-    window.SelectList = function (options) {
-      this.settings = $.extend(window.SelectList.defaults, options);
-
-      this.collection = new SelectListCollection();
-
-      this.view = new SelectListView({
-        el: this.settings.el,
-        collection: this.collection,
-        settings: this.settings
-      });
-
-      this.view.render();
-      this.filter('selected');
-      return this;
-    };
-
-
-    /*
-     * SelectList API Methods
-     */
-
-    window.SelectList.prototype.filter = function (filter) {
-      this.view.filterBySelection(filter);
-      return this;
-    };
-
-    window.SelectList.prototype.search = function (query) {
-      this.view.searchByQuery(query);
-      return this;
-    };
-
-
-    /*
-     * SelectList Defaults
-     */
-
-    window.SelectList.defaults = {
-      width: '50%',
-      height: 400,
-
-      format: function (item) {
-        return item.value;
-      },
-
-      labels: {
-        selected: 'Selected',
-        deselected: 'Deselected',
-        all: 'All'
-      },
-
-      tooltips: {
-        select: 'Click this to select item',
-        deselect: 'Click this to deselect item'
-      },
-
-      errorMessage: 'Something gone wrong, try to reload the page and try again.'
-    };
-
-  })(jQuery);
-
-});