aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/nav
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-05-18 16:29:19 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-05-19 18:20:09 +0200
commite59e4aa2c06a57566f148f4217d286461231ae58 (patch)
treead0918eb06b2474a6c71997b7ff573bbb93e0170 /server/sonar-web/src/main/js/apps/nav
parent8d82c9cf9ba88c1517604b4063ad0fc2a9a1bb9a (diff)
downloadsonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.tar.gz
sonarqube-e59e4aa2c06a57566f148f4217d286461231ae58.zip
change web structure: separate components
Diffstat (limited to 'server/sonar-web/src/main/js/apps/nav')
-rw-r--r--server/sonar-web/src/main/js/apps/nav/app.js79
-rw-r--r--server/sonar-web/src/main/js/apps/nav/context-navbar-view.js82
-rw-r--r--server/sonar-web/src/main/js/apps/nav/global-navbar-view.js97
-rw-r--r--server/sonar-web/src/main/js/apps/nav/search-view.js250
-rw-r--r--server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js32
-rw-r--r--server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js30
-rw-r--r--server/sonar-web/src/main/js/apps/nav/state.js51
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs124
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs105
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs22
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs8
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs86
-rw-r--r--server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs57
16 files changed, 1026 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/nav/app.js b/server/sonar-web/src/main/js/apps/nav/app.js
new file mode 100644
index 00000000000..5d45dd82f97
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/app.js
@@ -0,0 +1,79 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './state',
+ './global-navbar-view',
+ './context-navbar-view',
+ './settings-navbar-view',
+ 'components/workspace/main'
+], function (State, GlobalNavbarView, ContextNavbarView, SettingsNavbarView) {
+
+ var $ = jQuery,
+ App = new Marionette.Application(),
+ state = new State();
+
+ state.set(window.navbarOptions.toJSON());
+
+ App.on('start', function () {
+ state.fetchGlobal();
+
+ this.navbarView = new GlobalNavbarView({
+ app: App,
+ el: $('.navbar-global'),
+ model: state
+ });
+ this.navbarView.render();
+
+ if (state.get('space') === 'component') {
+ state.fetchComponent();
+ this.contextNavbarView = new ContextNavbarView({
+ app: App,
+ el: $('.navbar-context'),
+ model: state
+ });
+ this.contextNavbarView.render();
+ }
+
+ if (state.get('space') === 'settings') {
+ state.fetchSettings();
+ this.settingsNavbarView = new SettingsNavbarView({
+ app: App,
+ el: $('.navbar-context'),
+ model: state
+ });
+ this.settingsNavbarView.render();
+ }
+
+ $(window).on('keypress', function (e) {
+ var tagName = e.target.tagName;
+ if (tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') {
+ var code = e.keyCode || e.which;
+ if (code === 63) {
+ App.navbarView.showShortcutsHelp();
+ }
+ }
+ });
+ });
+
+ window.requestMessages().done(function () {
+ App.start();
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js
new file mode 100644
index 00000000000..07eb0cd159a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/context-navbar-view.js
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery,
+ MORE_URLS = [
+ '/dashboards', '/plugins/resource'
+ ],
+ SETTINGS_URLS = [
+ '/project/settings', '/project/profile', '/project/qualitygate', '/manual_measures/index',
+ '/action_plans/index', '/project/links', '/project_roles/index', '/project/history', '/project/key',
+ '/project/deletion'
+ ];
+
+ return Marionette.ItemView.extend({
+ template: Templates['nav-context-navbar'],
+
+ modelEvents: {
+ 'change:component': 'render'
+ },
+
+ events: {
+ 'click .js-favorite': 'onFavoriteClick'
+ },
+
+ onRender: function () {
+ this.$('[data-toggle="tooltip"]').tooltip({
+ container: 'body'
+ });
+ },
+
+ onFavoriteClick: function () {
+ var that = this,
+ url = baseUrl + '/favourites/toggle/' + this.model.get('contextId'),
+ isContextFavorite = this.model.get('isContextFavorite');
+ this.model.set({ isContextFavorite: !isContextFavorite });
+ return $.post(url).fail(function () {
+ that.model.set({ isContextFavorite: isContextFavorite });
+ });
+ },
+
+ serializeData: function () {
+ var href = window.location.href,
+ search = window.location.search,
+ isMoreActive = _.some(MORE_URLS, function (url) {
+ return href.indexOf(url) !== -1;
+ }) || (href.indexOf('/dashboard') !== -1 && search.indexOf('did=') !== -1),
+ isSettingsActive = _.some(SETTINGS_URLS, function (url) {
+ return href.indexOf(url) !== -1;
+ }),
+ isOverviewActive = !isMoreActive && href.indexOf('/dashboard') !== -1 && search.indexOf('did=') === -1;
+ return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
+ canManageContextDashboards: !!window.SS.user,
+ contextKeyEncoded: encodeURIComponent(this.model.get('componentKey')),
+
+ isOverviewActive: isOverviewActive,
+ isSettingsActive: isSettingsActive,
+ isMoreActive: isMoreActive
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js
new file mode 100644
index 00000000000..9ee43d9fdfe
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/global-navbar-view.js
@@ -0,0 +1,97 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './search-view',
+ './shortcuts-help-view',
+ './templates'
+], function (SearchView, ShortcutsHelpView) {
+
+ return Marionette.Layout.extend({
+ template: Templates['nav-global-navbar'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ regions: {
+ searchRegion: '.js-search-region'
+ },
+
+ events: {
+ 'click .js-login': 'onLoginClick',
+ 'click .js-favorite': 'onFavoriteClick',
+ 'show.bs.dropdown .js-search-dropdown': 'onSearchDropdownShow',
+ 'hidden.bs.dropdown .js-search-dropdown': 'onSearchDropdownHidden',
+ 'click .js-shortcuts': 'onShortcutsClick'
+ },
+
+ onRender: function () {
+ var that = this;
+ if (this.model.has('space')) {
+ this.$el.addClass('navbar-' + this.model.get('space'));
+ }
+ this.$el.addClass('navbar-fade');
+ setTimeout(function () {
+ that.$el.addClass('in');
+ }, 0);
+ },
+
+ onLoginClick: function () {
+ var returnTo = window.location.pathname + window.location.search;
+ window.location = baseUrl + '/sessions/new?return_to=' + encodeURIComponent(returnTo) + window.location.hash;
+ return false;
+ },
+
+ onSearchDropdownShow: function () {
+ var that = this;
+ this.searchRegion.show(new SearchView({
+ model: this.model,
+ hide: function () {
+ that.$('.js-search-dropdown-toggle').dropdown('toggle');
+ }
+ }));
+ },
+
+ onSearchDropdownHidden: function () {
+ this.searchRegion.reset();
+ },
+
+ onShortcutsClick: function () {
+ this.showShortcutsHelp();
+ },
+
+ showShortcutsHelp: function () {
+ new ShortcutsHelpView({ shortcuts: this.model.get('shortcuts') }).render();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
+ user: window.SS.user,
+ userName: window.SS.userName,
+ isUserAdmin: window.SS.isUserAdmin,
+
+ canManageGlobalDashboards: !!window.SS.user,
+ canManageIssueFilters: !!window.SS.user,
+ canManageMeasureFilters: !!window.SS.user
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/search-view.js b/server/sonar-web/src/main/js/apps/nav/search-view.js
new file mode 100644
index 00000000000..57daa695031
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/search-view.js
@@ -0,0 +1,250 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/selectable-collection-view',
+ './templates'
+], function (SelectableCollectionView) {
+
+ var $ = jQuery,
+
+ SearchItemView = Marionette.ItemView.extend({
+ tagName: 'li',
+ template: Templates['nav-search-item'],
+
+ select: function () {
+ this.$el.addClass('active');
+ },
+
+ deselect: function () {
+ this.$el.removeClass('active');
+ },
+
+ submit: function () {
+ this.$('a')[0].click();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ index: this.options.index
+ });
+ }
+ }),
+
+ SearchEmptyView = Marionette.ItemView.extend({
+ tagName: 'li',
+ template: Templates['nav-search-empty']
+ }),
+
+ SearchResultsView = SelectableCollectionView.extend({
+ className: 'menu',
+ tagName: 'ul',
+ itemView: SearchItemView,
+ emptyView: SearchEmptyView
+ });
+
+ return Marionette.Layout.extend({
+ className: 'navbar-search',
+ tagName: 'form',
+ template: Templates['nav-search'],
+
+ regions: {
+ resultsRegion: '.js-search-results'
+ },
+
+ events: {
+ 'submit': 'onSubmit',
+ 'keydown .js-search-input': 'onKeyDown',
+ 'keyup .js-search-input': 'debouncedOnKeyUp'
+ },
+
+ initialize: function () {
+ var that = this;
+ this.results = new Backbone.Collection();
+ this.favorite = [];
+ if (window.SS.user) {
+ this.fetchFavorite().always(function () {
+ that.resetResultsToDefault();
+ });
+ } else {
+ this.resetResultsToDefault();
+ }
+ this.resultsView = new SearchResultsView({ collection: this.results });
+ this.debouncedOnKeyUp = _.debounce(this.onKeyUp, 400);
+ this._bufferedValue = '';
+ },
+
+ onRender: function () {
+ var that = this;
+ this.resultsRegion.show(this.resultsView);
+ setTimeout(function () {
+ that.$('.js-search-input').focus();
+ }, 0);
+ },
+
+ onKeyDown: function (e) {
+ if (e.keyCode === 38) {
+ this.resultsView.selectPrev();
+ return false;
+ }
+ if (e.keyCode === 40) {
+ this.resultsView.selectNext();
+ return false;
+ }
+ if (e.keyCode === 13) {
+ this.resultsView.submitCurrent();
+ return false;
+ }
+ if (e.keyCode === 27) {
+ this.options.hide();
+ return false;
+ }
+ },
+
+ onKeyUp: function () {
+ var value = this.$('.js-search-input').val();
+ if (value === this._bufferedValue) {
+ return;
+ }
+ this._bufferedValue = this.$('.js-search-input').val();
+ if (this.searchRequest != null) {
+ this.searchRequest.abort();
+ }
+ this.searchRequest = this.search(value);
+ },
+
+ onSubmit: function () {
+ return false;
+ },
+
+ fetchFavorite: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/favourites').done(function (r) {
+ that.favorite = r.map(function (f) {
+ var isFile = ['FIL', 'UTS'].indexOf(f.qualifier) !== -1;
+ return {
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(f.key) + dashboardParameters(true),
+ name: isFile ? window.collapsedDirFromPath(f.lname) + window.fileFromPath(f.lname) : f.name,
+ icon: 'favorite'
+ };
+ });
+ that.favorite = _.sortBy(that.favorite, 'name');
+ });
+ },
+
+ resetResultsToDefault: function () {
+ var recentHistory = JSON.parse(localStorage.getItem('sonar_recent_history')),
+ history = (recentHistory || []).map(function (historyItem, index) {
+ return {
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(historyItem.key) + dashboardParameters(true),
+ name: historyItem.name,
+ q: historyItem.icon,
+ extra: index === 0 ? t('browsed_recently') : null
+ };
+ }),
+ favorite = _.first(this.favorite, 6).map(function (f, index) {
+ return _.extend(f, { extra: index === 0 ? t('favorite') : null });
+ }),
+ qualifiers = this.model.get('qualifiers').map(function (q, index) {
+ return {
+ url: baseUrl + '/all_projects?qualifier=' + encodeURIComponent(q),
+ name: t('qualifiers.all', q),
+ extra: index === 0 ? '' : null
+ };
+ });
+ this.results.reset([].concat(history, favorite, qualifiers));
+ },
+
+ search: function (q) {
+ if (q.length < 2) {
+ this.resetResultsToDefault();
+ return;
+ }
+ var that = this,
+ url = baseUrl + '/api/components/suggestions',
+ options = { s: q };
+ return $.get(url, options).done(function (r) {
+ var collection = [];
+ r.results.forEach(function (domain) {
+ domain.items.forEach(function (item, index) {
+ collection.push(_.extend(item, {
+ q: domain.q,
+ extra: index === 0 ? domain.name : null,
+ url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(item.key) + dashboardParameters(true)
+ }));
+ });
+ });
+ that.results.reset([].concat(
+ that.getNavigationFindings(q),
+ that.getGlobalDashboardFindings(q),
+ that.getFavoriteFindings(q),
+ collection
+ ));
+ });
+ },
+
+ getNavigationFindings: function (q) {
+ var DEFAULT_ITEMS = [
+ { name: t('issues.page'), url: baseUrl + '/issues/search' },
+ { name: t('layout.measures'), url: baseUrl + '/measures/search?qualifiers[]=TRK' },
+ { name: t('coding_rules.page'), url: baseUrl + '/coding_rules' },
+ { name: t('quality_profiles.page'), url: baseUrl + '/profiles' },
+ { name: t('quality_gates.page'), url: baseUrl + '/quality_gates' },
+ { name: t('comparison_global.page'), url: baseUrl + '/comparison' },
+ { name: t('dependencies.page'), url: baseUrl + '/dependencies' }
+ ],
+ customItems = [];
+ if (window.SS.isUserAdmin) {
+ customItems.push({ name: t('layout.settings'), url: baseUrl + '/settings' });
+ }
+ var findings = [].concat(DEFAULT_ITEMS, customItems).filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('navigation');
+ }
+ return _.first(findings, 6);
+ },
+
+ getGlobalDashboardFindings: function (q) {
+ var dashboards = this.model.get('globalDashboards') || [],
+ items = dashboards.map(function (d) {
+ return { name: d.name, url: baseUrl + '/dashboard/index?did=' + encodeURIComponent(d.key) };
+ });
+ var findings = items.filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('dashboard.global_dashboards');
+ }
+ return _.first(findings, 6);
+ },
+
+ getFavoriteFindings: function (q) {
+ var findings = this.favorite.filter(function (f) {
+ return f.name.match(new RegExp(q, 'i'));
+ });
+ if (findings.length > 0) {
+ findings[0].extra = t('favorite');
+ }
+ return _.first(findings, 6);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js b/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js
new file mode 100644
index 00000000000..6134e229fd1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/settings-navbar-view.js
@@ -0,0 +1,32 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['nav-settings-navbar'],
+
+ modelEvents: {
+ 'change:settings': 'render'
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js b/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js
new file mode 100644
index 00000000000..b054b7c6150
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/shortcuts-help-view.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define([
+ 'components/common/modals',
+ './templates'
+], function (ModalView) {
+
+ return ModalView.extend({
+ className: 'modal modal-large',
+ template: Templates['nav-shortcuts-help']
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/state.js b/server/sonar-web/src/main/js/apps/nav/state.js
new file mode 100644
index 00000000000..f41557663ee
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/state.js
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+define(function () {
+
+ var $ = jQuery;
+
+ return Backbone.Model.extend({
+
+ fetchGlobal: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/navigation/global').done(function (r) {
+ that.set(r);
+ });
+ },
+
+ fetchComponent: function () {
+ var that = this,
+ url = baseUrl + '/api/navigation/component',
+ data = { componentKey: this.get('componentKey') };
+ return $.get(url, data).done(function (r) {
+ that.set({ component: r });
+ });
+ },
+
+ fetchSettings: function () {
+ var that = this;
+ return $.get(baseUrl + '/api/navigation/settings').done(function (r) {
+ that.set({ settings: r });
+ });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs b/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs
new file mode 100644
index 00000000000..fcc91b8f24b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/_nav-logo.hbs
@@ -0,0 +1 @@
+<img src="{{link '/images/logo.svg'}}" alt="{{t 'layout.sonar.slogan'}}" title="{{t 'layout.sonar.slogan'}}">
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs b/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs
new file mode 100644
index 00000000000..df44ac06af2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/_nav-navbar-label.hbs
@@ -0,0 +1 @@
+{{#if labelLocalized}}{{labelLocalized}}{{else}}{{t label}}{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs
new file mode 100644
index 00000000000..11ef4f53ad7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-context-navbar.hbs
@@ -0,0 +1,124 @@
+<div class="container">
+ {{#if component.canBeFavorite}}
+ <div class="navbar-context-favorite">
+ <a class="js-favorite {{#if component.isFavorite}}icon-favorite{{else}}icon-not-favorite{{/if}}"></a>
+ </div>
+ {{/if}}
+ <ul class="nav navbar-nav nav-crumbs">
+ {{#each component.breadcrumbs}}
+ <li>
+ <a href="{{componentPermalink key}}">
+ {{qualifierIcon qualifier}}&nbsp;{{name}}
+ </a>
+ </li>
+ {{/each}}
+ </ul>
+
+ <div class="navbar-right navbar-context-meta">
+ {{#if component.version}}Version {{component.version}}{{/if}}
+ {{#all component.version component.snapshotDate}}/{{/all}}
+ {{#if component.snapshotDate}}{{dt component.snapshotDate}}{{/if}}
+ </div>
+
+ <ul class="nav navbar-nav nav-tabs">
+ <li {{#if isOverviewActive}}class="active"{{/if}}>
+ <a href="{{componentPermalink component.key}}">{{t 'overview.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/components'}}class="active"{{/isActiveLink}}>
+ <a href="{{componentBrowsePermalink component.key}}">{{t 'components.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/component_issues'}}class="active"{{/isActiveLink}}>
+ <a href="{{componentIssuesPermalink component.key}}">{{t 'issues.page'}}</a>
+ </li>
+ {{#if component.configuration.showSettings}}
+ <li class="dropdown {{#if isSettingsActive}}active{{/if}}">
+ <a class="dropdown-toggle navbar-admin-link" data-toggle="dropdown" href="#">{{t 'layout.settings'}}&nbsp;<i
+ class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/project/settings?id=' contextKeyEncoded}}">{{t 'project_settings.page'}}</a>
+ </li>
+ {{#if component.configuration.showQualityProfiles}}
+ <li>
+ <a href="{{link '/project/profile?id=' contextKeyEncoded}}">{{t 'project_quality_profiles.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showQualityGates}}
+ <li>
+ <a href="{{link '/project/qualitygate?id=' contextKeyEncoded}}">{{t 'project_quality_gate.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showManualMeasures}}
+ <li>
+ <a href="{{link '/manual_measures/index?id=' contextKeyEncoded}}">{{t 'manual_measures.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showActionPlans}}
+ <li>
+ <a href="{{link '/action_plans/index?id=' contextKeyEncoded}}">{{t 'action_plans.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showLinks}}
+ <li>
+ <a href="{{link '/project/links?id=' contextKeyEncoded}}">{{t 'project_links.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showPermissions}}
+ <li>
+ <a href="{{link '/project_roles/index?id=' contextKeyEncoded}}">{{t 'permissions.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showHistory}}
+ <li>
+ <a href="{{link '/project/history?id=' contextKeyEncoded}}">{{t 'project_history.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showUpdateKey}}
+ <li>
+ <a href="{{link '/project/key?id=' contextKeyEncoded}}">{{t 'update_key.page'}}</a>
+ </li>
+ {{/if}}
+ {{#if component.configuration.showDeletion}}
+ <li>
+ <a href="{{link '/project/deletion?id=' contextKeyEncoded}}">{{t 'deletion.page'}}</a>
+ </li>
+ {{/if}}
+ {{#each component.configuration.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ {{/if}}
+ <li class="dropdown {{#if isMoreActive}}active{{/if}}">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li class="dropdown-header">{{t 'layout.dashboards'}}</li>
+ {{#withoutFirst component.dashboards}}
+ <li>
+ <a href="{{componentDashboardPermalink ../component.key key}}">{{dashboardL10n name}}</a>
+ </li>
+ {{/withoutFirst}}
+ {{#if canManageContextDashboards}}
+ <li class="small-divider"></li>
+ <li>
+ <a href="{{link '/dashboards?resource=' contextKeyEncoded}}">{{t 'dashboard.manage_dashboards'}}</a>
+ </li>
+ {{/if}}
+ <li class="divider"></li>
+ <li class="dropdown-header">Tools</li>
+ {{#if component.isComparable}}
+ <li>
+ <a href="{{link '/comparison/index?resource=' contextKeyEncoded}}">{{t 'comparison.page'}}</a>
+ </li>
+ {{/if}}
+ {{#each component.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs
new file mode 100644
index 00000000000..fcaf9cc2672
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-global-navbar.hbs
@@ -0,0 +1,105 @@
+<div class="container">
+ <div class="navbar-header">
+ <a class="navbar-brand {{#if logoUrl}}navbar-brand-custom{{/if}}" href="{{link '/'}}">
+ {{#if logoUrl}}
+ <img src="{{logoUrl}}" {{#if logoWidth}}width="{{logoWidth}}"{{/if}} height="30"
+ alt="{{t 'layout.sonar.slogan'}}" title="{{t 'layout.sonar.slogan'}}">
+ {{else}}
+ {{> '_nav-logo'}}
+ {{/if}}
+ </a>
+ </div>
+
+ <ul class="nav navbar-nav">
+ <li class="dropdown t-dashboards">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'layout.dashboards'}}&nbsp;<span class="icon-dropdown"></span>
+ </a>
+ <ul class="dropdown-menu">
+ {{#each globalDashboards}}
+ <li>
+ <a href="{{link '/dashboard/index?did=' key}}">{{dashboardL10n name}}</a>
+ </li>
+ {{/each}}
+ {{#if canManageGlobalDashboards}}
+ <li class="divider"></li>
+ <li>
+ <a href="{{link '/dashboards'}}">{{t 'dashboard.manage_dashboards'}}</a>
+ </li>
+ {{/if}}
+ </ul>
+ </li>
+ <li {{#isActiveLink '/issues'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/issues/search'}}">{{t 'issues.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/measures'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/measures/search?qualifiers[]=TRK'}}">{{t 'layout.measures'}}</a>
+ </li>
+ <li {{#isActiveLink '/coding_rules'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/coding_rules'}}">{{t 'coding_rules.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/profiles'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/profiles'}}">{{t 'quality_profiles.page'}}</a>
+ </li>
+ <li {{#isActiveLink '/quality_gates'}}class="active"{{/isActiveLink}}>
+ <a href="{{link '/quality_gates'}}">{{t 'quality_gates.page'}}</a>
+ </li>
+ {{#if isUserAdmin}}
+ <li {{#isActiveLink '/settings'}}class="active"{{/isActiveLink}}>
+ <a class="navbar-admin-link" href="{{link '/settings'}}">{{t 'layout.settings'}}</a>
+ </li>
+ {{/if}}
+ <li class="dropdown t-more">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/comparison'}}">{{t 'comparison_global.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/dependencies'}}">{{t 'dependencies.page'}}</a>
+ </li>
+ {{#each globalPages}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ </ul>
+
+ <ul class="nav navbar-nav navbar-right">
+ {{#if user}}
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{userName}}&nbsp;<span class="icon-dropdown"></span>
+ </a>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a href="{{link '/account/index'}}">{{t 'layout.user_panel.my_profile'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/sessions/logout'}}"
+ onclick="if (sonarRecentHistory) { sonarRecentHistory.clear(); }">{{t 'layout.logout'}}</a>
+ </li>
+ </ul>
+ </li>
+ {{else}}
+ <li>
+ <a class="js-login" href="{{link '/sessions/new'}}">{{t 'layout.login'}}</a>
+ </li>
+ {{/if}}
+ <li class="dropdown js-search-dropdown">
+ <a class="dropdown-toggle navbar-search-dropdown js-search-dropdown-toggle" data-toggle="dropdown" href="#">
+ <i class="icon-search navbar-icon"></i>&nbsp;<span class="icon-dropdown"></span>
+ </a>
+
+ <div class="js-search-region dropdown-menu dropdown-menu-right"></div>
+ </li>
+ <li>
+ <a class="js-shortcuts" href="#">
+ <i class="icon-help navbar-icon"></i>
+ </a>
+ </li>
+ </ul>
+
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs
new file mode 100644
index 00000000000..fb76e686612
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-empty.hbs
@@ -0,0 +1 @@
+<span class="note">{{t 'no_results'}}</span>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs
new file mode 100644
index 00000000000..855b5175187
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search-item.hbs
@@ -0,0 +1,22 @@
+{{#notNull extra}}
+ {{#gt index 0}}
+ <div class="divider"></div>
+ {{/gt}}
+ {{#if extra}}
+ <div class="dropdown-header">{{extra}}</div>
+ {{/if}}
+{{/notNull}}
+
+<a href="{{url}}" title="{{name}}">
+ {{#if icon}}<i class="icon-{{icon}} text-text-bottom"></i>{{/if}}
+ {{#if q}}{{qualifierIcon q}}{{/if}}
+ {{#eq q 'FIL'}}
+ {{collapsedDirFromPath name}}{{fileFromPath name}}
+ {{else}}
+ {{#eq q 'UTS'}}
+ {{collapsedDirFromPath name}}{{fileFromPath name}}
+ {{else}}
+ {{name}}
+ {{/eq}}
+ {{/eq}}
+</a>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs
new file mode 100644
index 00000000000..68e1f3ad168
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-search.hbs
@@ -0,0 +1,8 @@
+<i class="navbar-search-icon icon-search"></i>
+
+<input class="navbar-search-input js-search-input" type="search" name="q" placeholder="{{t 'search_verb'}}"
+ maxlength="30" autocomplete="off">
+
+<div class="js-search-results"></div>
+
+<div class="note navbar-search-subtitle">{{t 'search.shortcut'}}</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
new file mode 100644
index 00000000000..5cc7dbbc1ed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-settings-navbar.hbs
@@ -0,0 +1,86 @@
+<div class="container">
+ <ul class="nav navbar-nav nav-crumbs">
+ <li>
+ <a href="{{link '/settings'}}">{{t 'layout.settings'}}</a>
+ </li>
+ </ul>
+
+ <ul class="nav navbar-nav nav-tabs">
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.project_settings'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/settings/index'}}">{{t 'settings.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/metrics/index'}}">{{t 'manual_metrics.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/admin_dashboards/index'}}">{{t 'default_dashboards.page'}}</a>
+ </li>
+ {{#each settings.extensions}}
+ <li>
+ <a href="{{link url}}">{{name}}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.security'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a href="{{link '/users'}}">{{t 'users.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/groups/index'}}">{{t 'user_groups.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/roles/global'}}">{{t 'global_permissions.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/roles/projects'}}">{{t 'roles.page'}}</a>
+ </li>
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.projects'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ {{#if settings.showProvisioning}}
+ <li>
+ <a href="{{link '/provisioning'}}">{{t 'provisioning.page'}}</a>
+ </li>
+ {{/if}}
+ <li>
+ <a href="{{link '/bulk_deletion'}}">{{t 'bulk_deletion.page'}}</a>
+ </li>
+ <li>
+ <a href="{{link '/analysis_reports'}}">{{t 'analysis_reports.page'}}</a>
+ </li>
+ </ul>
+ </li>
+
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+ {{t 'sidebar.system'}}&nbsp;<i class="icon-dropdown"></i>
+ </a>
+ <ul class="dropdown-menu">
+ {{#if settings.showUpdateCenter}}
+ <li>
+ <a href="{{link '/updatecenter'}}">{{t 'update_center.page'}}</a>
+ </li>
+ {{/if}}
+ <li>
+ <a href="{{link '/system'}}">{{t 'system_info.page'}}</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+</div>
diff --git a/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
new file mode 100644
index 00000000000..9457d1b7762
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/nav/templates/nav-shortcuts-help.hbs
@@ -0,0 +1,57 @@
+<div class="modal-head">
+ <h2>{{t 'help'}}</h2>
+</div>
+
+<div class="modal-body modal-container">
+ <div class="spacer-bottom">
+ <a href="http://www.sonarqube.org" target="sonar">Community</a> -
+ <a href="http://www.sonarqube.org/documentation" target="sonar_doc">Documentation</a> -
+ <a href="http://www.sonarqube.org/support" target="support">Get Support</a> -
+ <a href="http://redirect.sonarsource.com/doc/plugin-library.html" target="plugins">Plugins</a> -
+ <a href="{{link '/api_documentation'}}">Web Service API</a>
+ </div>
+
+ <h2 class="spacer-top spacer-bottom">{{t 'shortcuts.modal_title'}}</h2>
+
+ <div class="columns">
+ <div class="column-half">
+ <div class="spacer-bottom">
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.global'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">s</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.search'}}</li>
+ <li><span class="shortcut-button">?</span> &nbsp;&nbsp; {{t 'shortcuts.section.global.shortcuts'}}</li>
+ </ul>
+ </div>
+
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.rules'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">&uparrow;</span> <span
+ class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.navigate_between_rules'}}</li>
+ <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.open_details'}}</li>
+ <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.rules.return_to_list'}}</li>
+ </ul>
+ </div>
+
+ <div class="column-half">
+ <h3 class="shortcuts-section-title">{{t 'shortcuts.section.issues'}}</h3>
+ <ul class="shortcuts-list">
+ <li><span class="shortcut-button">&uparrow;</span> <span
+ class="shortcut-button">&darr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.navigate_between_issues'}}
+ </li>
+ <li><span class="shortcut-button">&rarr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.open_details'}}</li>
+ <li><span class="shortcut-button">&larr;</span> &nbsp;&nbsp; {{t 'shortcuts.section.issues.return_to_list'}}</li>
+ <li><span class="shortcut-button">f</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.do_transition'}}</li>
+ <li><span class="shortcut-button">a</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign'}}</li>
+ <li><span class="shortcut-button">m</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.assign_to_me'}}</li>
+ <li><span class="shortcut-button">p</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.plan'}}</li>
+ <li><span class="shortcut-button">i</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_severity'}}</li>
+ <li><span class="shortcut-button">c</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.comment'}}</li>
+ <li><span class="shortcut-button">t</span> &nbsp;&nbsp; {{t 'shortcuts.section.issue.change_tags'}}</li>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<div class="modal-foot">
+ <a class="js-modal-close" href="#">{{t 'close'}}</a>
+</div>