]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6041 New webapp layout
authorStas Vilchik <vilchiks@gmail.com>
Tue, 20 Jan 2015 15:39:07 +0000 (16:39 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 21 Jan 2015 08:16:59 +0000 (09:16 +0100)
39 files changed:
server/sonar-web/Gruntfile.coffee
server/sonar-web/src/main/hbs/nav/_nav-logo.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/_nav-navbar-label.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/nav-context-navbar.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/nav-global-navbar.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/nav-search-empty.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/nav-search-item.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/nav/nav-search.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/application.js
server/sonar-web/src/main/js/common/selectable-collection-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/nav/app.js [new file with mode: 0644]
server/sonar-web/src/main/js/nav/context-navbar-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/nav/global-navbar-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/nav/search-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/third-party/bootstrap/dropdown.js [new file with mode: 0644]
server/sonar-web/src/main/less/components.less
server/sonar-web/src/main/less/components/dropdowns.less [new file with mode: 0644]
server/sonar-web/src/main/less/components/menu.less [new file with mode: 0644]
server/sonar-web/src/main/less/components/search-navigator.less
server/sonar-web/src/main/less/dashboard.less
server/sonar-web/src/main/less/layout.less
server/sonar-web/src/main/less/navbar.less [new file with mode: 0644]
server/sonar-web/src/main/less/style.less
server/sonar-web/src/main/less/ui.less
server/sonar-web/src/main/less/variables.less
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/components_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/views/account/index.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/dependencies/index.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_breadcrumb.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_context.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_global.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_settings.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_recent_history.html.erb [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/application.html.erb
server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/index.html.erb

index 60b9b6b3e870142be464b807fb957cd9af111348..2ce259390f6f7beacf2604c76292149cd4d507d7 100644 (file)
@@ -91,6 +91,7 @@ module.exports = (grunt) ->
             '<%= pkg.assets %>js/third-party/numeral.js'
             '<%= pkg.assets %>js/third-party/numeral-languages.js'
             '<%= pkg.assets %>js/third-party/bootstrap/tooltip.js'
+            '<%= pkg.assets %>js/third-party/bootstrap/dropdown.js'
             '<%= pkg.assets %>js/select2-jquery-ui-fix.js'
             '<%= pkg.assets %>js/widgets/base.js'
             '<%= pkg.assets %>js/widgets/widget.js'
@@ -134,6 +135,7 @@ module.exports = (grunt) ->
             '<%= pkg.assets %>js/third-party/numeral.js'
             '<%= pkg.assets %>js/third-party/numeral-languages.js'
             '<%= pkg.assets %>js/third-party/bootstrap/tooltip.js'
+            '<%= pkg.assets %>js/third-party/bootstrap/dropdown.js'
             '<%= pkg.assets %>js/select2-jquery-ui-fix.js'
             '<%= pkg.assets %>js/widgets/base.js'
             '<%= pkg.assets %>js/widgets/widget.js'
@@ -235,6 +237,10 @@ module.exports = (grunt) ->
         name: 'analysis-reports/app'
         out: '<%= pkg.assets %>build/js/analysis-reports/app.js'
 
+      nav: options:
+        name: 'nav/app'
+        out: '<%= pkg.assets %>build/js/nav/app.js'
+
 
     handlebars:
       options:
@@ -285,6 +291,9 @@ module.exports = (grunt) ->
           '<%= pkg.assets %>js/templates/analysis-reports.js': [
             '<%= pkg.sources %>hbs/analysis-reports/**/*.hbs'
           ]
+          '<%= pkg.assets %>js/templates/nav.js': [
+            '<%= pkg.sources %>hbs/nav/**/*.hbs'
+          ]
 
 
     clean:
diff --git a/server/sonar-web/src/main/hbs/nav/_nav-logo.hbs b/server/sonar-web/src/main/hbs/nav/_nav-logo.hbs
new file mode 100644 (file)
index 0000000..fcb273a
--- /dev/null
@@ -0,0 +1,18 @@
+<div class="sq-logo">
+  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100" height="25">
+    <g opacity="1">
+      <path class="sq-logo-arc sq-logo-arc1" d="m96.96313,21.20284l-1.22246,0c0,-9.83637 -8.12817,-17.87548 -18.16693,-17.87548l0,-1.26294c10.69453,0 19.41368,8.6382 19.41368,19.13842l-0.02429,0z"></path>
+      <path class="sq-logo-arc sq-logo-arc2" d="m97.81319,14.62098c-1.47343,-6.17708 -6.47662,-11.36647 -12.79134,-13.15564l0.28336,-1.00388c6.64664,1.93489 11.94128,7.38335 13.47137,13.96522l-0.96339,0.1943z"></path>
+      <path class="sq-logo-arc sq-logo-arc3" d="m99.19757,8.84059c-1.522,-3.32736 -4.07217,-6.18517 -7.26192,-8.11197l0.40479,-0.72862c3.31117,1.99156 6.03945,5.05177 7.56956,8.50866l-0.71243,0.33193z"></path>
+      <path class="sq-logo-letter" d="m0,20.74138c0.72862,0.39669 1.86203,0.72862 3.12497,0.72862c2.72828,0 4.12075,-1.32771 4.12075,-3.12497c-0.02429,-1.39247 -0.80148,-2.38826 -2.59065,-2.98734c-1.18198,-0.40479 -1.5301,-0.66385 -1.5301,-1.13341c-0.02429,-0.46146 0.39669,-0.72862 1.12531,-0.72862c0.79339,0 1.66773,0.26716 2.06442,0.52623l0.51813,-2.05633c-0.59099,-0.26716 -1.57868,-0.59909 -2.71209,-0.59909c-2.35587,0 -3.85359,1.39247 -3.85359,3.18974c-0.04857,1.13341 0.72862,2.19396 2.72018,2.85781c1.09293,0.39669 1.40057,0.59909 1.40057,1.13341c-0.0081,0.53432 -0.39669,0.79339 -1.26294,0.79339c-0.90673,0 -2.06442,-0.39669 -2.59065,-0.72862l-0.53432,2.12919z"></path>
+      <path class="sq-logo-letter" d="m13.10707,13.56043c1.352,0 1.9187,1.39247 1.9187,2.85781c-0.01619,1.73249 -0.73672,2.85781 -1.92679,2.85781c-1.27104,0 -1.93489,-1.19817 -1.93489,-2.85781c-0.01619,-1.39248 0.53432,-2.85781 1.93489,-2.85781m0.10524,-2.19396c-3.10068,0 -5.15701,1.99156 -5.15701,5.11653c0.01619,3.12497 2.18586,4.987 4.9789,4.987c2.57446,0 5.05177,-1.65964 5.05177,-5.1813c0.01619,-2.85781 -1.9268,-4.92223 -4.84937,-4.92223l-0.02429,0"></path>
+      <path class="sq-logo-letter" d="m19.02508,21.20284l2.98734,0l0,-5.51323c0,-0.26716 -0.00809,-0.53432 0.12144,-0.73672c0.1862,-0.52623 0.66385,-1.12531 1.5301,-1.12531c1.05245,0 1.46534,0.86625 1.46534,2.05633l0.02429,5.31892l2.97925,0l0,-5.65085c0,-2.85781 -1.47343,-4.12075 -3.40832,-4.12075c-1.63535,0 -2.59065,0.93102 -2.98734,1.5301l-0.06477,0l-0.13763,-1.32771l-2.59065,0c0.04048,0.86625 0.06477,1.86203 0.06477,3.0602l0.01619,6.50901z"></path>
+      <path class="sq-logo-letter" d="m34.72279,17.74594c0,0.13763 -0.02429,0.33193 -0.08905,0.46956c-0.17001,0.66385 -0.80148,1.12531 -1.5301,1.12531c-0.65576,0 -1.19818,-0.39669 -1.19818,-1.12531c0.02429,-1.13341 1.26294,-1.5301 2.79304,-1.5301l0.02429,1.06054m2.8821,-2.19396c0,-2.25872 -0.97959,-4.18552 -4.16933,-4.18552c-1.78107,0 -3.12497,0.53432 -3.78882,0.86625l0.5748,1.9268c0.63147,-0.39669 1.68392,-0.72862 2.6797,-0.72862c1.44915,0 1.7325,0.72862 1.7325,1.26294l-0.00809,0.12953c-3.40023,0 -5.64276,1.19818 -5.64276,3.65929c0.0081,1.52201 1.19818,2.98734 3.12497,2.98734c1.11722,0 2.12919,-0.46146 2.72018,-1.19818l0.06477,0l0.17811,0.93101l2.6959,0c-0.12144,-0.52622 -0.14573,-1.39247 -0.14573,-2.32349l-0.01619,-3.32736z"></path>
+      <path class="sq-logo-letter" d="m39.07022,21.20284l2.97925,0l0,-4.84937c0,-0.20239 0.02429,-0.46955 0.02429,-0.66385c0.23478,-0.93101 1.00388,-1.5301 2.06442,-1.5301c0.34812,0 0.59909,0.06477 0.79338,0.06477l0.02429,-2.79304c-0.21049,0 -0.35621,-0.06477 -0.61528,-0.06477c-0.9553,0 -2.12919,0.59909 -2.59874,1.99156l-0.08096,0l-0.12143,-1.7244l-2.55017,0c0.05667,0.79339 0.09715,1.7244 0.09715,3.12497l-0.01619,6.44424z"></path>
+      <path class="sq-logo-letter" d="m64.89576,18.54743c0,0.99578 0.04858,1.9268 0.04858,2.65541l-1.5382,0l-0.09715,-1.59487l-0.04047,0c-0.46146,0.80148 -1.51391,1.86203 -3.24641,1.86203c-1.48962,0 -3.31927,-0.86625 -3.31927,-4.25028l0,-5.72372l1.77298,0l0,5.39179c0,1.85393 0.55051,3.11687 2.14538,3.11687c1.20628,0 1.99966,-0.85815 2.33159,-1.65963c0.11334,-0.26716 0.1943,-0.53432 0.1943,-0.86625l-0.01619,-5.98278l1.76488,0l0,7.05142z"></path>
+      <path class="sq-logo-letter" d="m68.01263,17.41402c0,0.20239 0.05667,0.39669 0.05667,0.59909c0.37241,1.26294 1.39247,2.12919 2.65541,2.12919c1.87012,0 2.99544,-1.5301 2.99544,-3.78882c-0.02428,-1.9268 -0.99577,-3.59453 -2.93066,-3.59453c-1.19009,0 -2.32349,0.80148 -2.65542,2.19396c-0.05668,0.20239 -0.12953,0.40479 -0.12953,0.73672l0.00809,1.7244m-1.82155,3.85359c0.04047,-0.66385 0.08096,-1.59487 0.08096,-2.45302l0,-11.77126l1.74059,0l0,6.12041l0.04047,0c0.62338,-1.06864 1.74059,-1.79726 3.33546,-1.79726c2.38825,0 4.12075,1.99156 4.05598,4.987c0.01619,3.4569 -2.19395,5.1813 -4.31505,5.1813c-1.42486,0 -2.52589,-0.52623 -3.2626,-1.86203l-0.07287,0l-0.08096,1.59487l-1.522,0z"></path>
+      <path class="sq-logo-letter" d="m83.28937,15.42246c0.01619,-1.13341 -0.46146,-2.85781 -2.46112,-2.85781c-1.77297,0 -2.59064,1.65963 -2.72018,2.85781l5.1813,0m-5.1813,1.26294c0.04047,2.39635 1.59486,3.39213 3.31927,3.39213c1.27914,0 2.06442,-0.26716 2.72827,-0.53432l0.27526,1.26294c-0.62337,0.26716 -1.67583,0.59909 -3.19784,0.59909c-3.02782,0 -4.79269,-1.9268 -4.79269,-4.85747c-0.01619,-2.92258 1.7325,-5.24607 4.52554,-5.24607c3.18164,0 4.05598,2.79304 4.05598,4.58221c-0.03238,0.33193 -0.07286,0.59909 -0.07286,0.80148l-6.84093,0z"></path>
+      <path class="sq-logo-letter" d="m52.59827,19.34081c-1.64344,1.13341 -3.86978,0.73672 -5.00319,-0.93102c-1.10103,-1.65963 -0.66385,-3.85359 0.93101,-4.9789c1.66773,-1.13341 3.91836,-0.67195 5.05177,0.93102c1.08483,1.65963 0.66385,3.91836 -0.99578,4.9789m2.23444,-5.84515c-1.58677,-2.33158 -4.76031,-2.92258 -7.15667,-1.32771c-2.3073,1.59487 -2.92258,4.78461 -1.32771,7.10809c1.40867,2.06442 4.05599,2.79305 6.3147,1.86203l1.95918,3.18974l1.57868,-1.06054l-1.98347,-3.2626c1.71631,-1.65963 2.03204,-4.37982 0.63957,-6.509l-0.02429,0"></path>
+    </g>
+  </svg>
+</div>
diff --git a/server/sonar-web/src/main/hbs/nav/_nav-navbar-label.hbs b/server/sonar-web/src/main/hbs/nav/_nav-navbar-label.hbs
new file mode 100644 (file)
index 0000000..df44ac0
--- /dev/null
@@ -0,0 +1 @@
+{{#if labelLocalized}}{{labelLocalized}}{{else}}{{t label}}{{/if}}
diff --git a/server/sonar-web/src/main/hbs/nav/nav-context-navbar.hbs b/server/sonar-web/src/main/hbs/nav/nav-context-navbar.hbs
new file mode 100644 (file)
index 0000000..df091ff
--- /dev/null
@@ -0,0 +1,38 @@
+<div class="container">
+  <ul class="nav navbar-nav">
+    {{#each breadcrumbs}}
+      <li>
+        <a href="{{link url}}">
+          {{qualifierIcon q}}&nbsp;{{name}}
+        </a>
+      </li>
+    {{/each}}
+  </ul>
+
+  <ul class="nav navbar-nav navbar-nav-pills navbar-right">
+    {{#each items}}
+      {{#notEmpty menu}}
+        <li class="dropdown {{#if active}}active{{/if}}">
+          <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+            {{> '_nav-navbar-label'}}&nbsp;<i class="icon-dropdown"></i>
+          </a>
+          <ul class="dropdown-menu dropdown-menu-right">
+            {{#each menu}}
+              <li {{#if active}}class="active"{{/if}}>
+                <a href="{{link url}}">
+                  {{> '_nav-navbar-label'}}
+                </a>
+              </li>
+            {{/each}}
+          </ul>
+        </li>
+      {{else}}
+        <li {{#if active}}class="active"{{/if}}>
+          <a href="{{link url}}">
+            {{> '_nav-navbar-label'}}
+          </a>
+        </li>
+      {{/notEmpty}}
+    {{/each}}
+  </ul>
+</div>
diff --git a/server/sonar-web/src/main/hbs/nav/nav-global-navbar.hbs b/server/sonar-web/src/main/hbs/nav/nav-global-navbar.hbs
new file mode 100644 (file)
index 0000000..5b00036
--- /dev/null
@@ -0,0 +1,63 @@
+<div class="container">
+  <ul class="nav navbar-nav">
+    {{#each items}}
+      {{#notEmpty menu}}
+        <li class="dropdown {{#if active}}active{{/if}}">
+          <a class="dropdown-toggle" data-toggle="dropdown" href="#">
+            {{> '_nav-navbar-label'}}&nbsp;<i class="icon-dropdown"></i>
+          </a>
+          <ul class="dropdown-menu">
+            {{#each menu}}
+              <li {{#if active}}class="active"{{/if}}>
+                <a href="{{link url}}">
+                  {{> '_nav-navbar-label'}}
+                </a>
+              </li>
+            {{/each}}
+          </ul>
+        </li>
+      {{else}}
+        <li {{#if active}}class="active"{{/if}}>
+          <a href="{{link url}}">
+            {{> '_nav-navbar-label'}}
+          </a>
+        </li>
+      {{/notEmpty}}
+    {{/each}}
+  </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 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>
+    {{#if isUserAdmin}}
+      <li>
+        <a href="{{link '/settings/index'}}"><i class="icon-settings navbar-icon"></i></a>
+      </li>
+    {{/if}}
+  </ul>
+
+</div>
diff --git a/server/sonar-web/src/main/hbs/nav/nav-search-empty.hbs b/server/sonar-web/src/main/hbs/nav/nav-search-empty.hbs
new file mode 100644 (file)
index 0000000..ccac483
--- /dev/null
@@ -0,0 +1 @@
+<span class="subtitle">{{t 'no_results'}}</span>
diff --git a/server/sonar-web/src/main/hbs/nav/nav-search-item.hbs b/server/sonar-web/src/main/hbs/nav/nav-search-item.hbs
new file mode 100644 (file)
index 0000000..1de73f2
--- /dev/null
@@ -0,0 +1,16 @@
+<a href="{{url}}" title="{{name}}">
+  {{#if extra}}
+    <span class="navbar-search-extra subtitle">{{extra}}</span>
+  {{/if}}
+  {{#if q}}{{qualifierIcon q}}{{/if}}
+  {{#if subtitle}}
+    {{title}}
+    <br>
+    {{#if extra}}
+      <span class="navbar-search-extra subtitle">&nbsp;</span>
+    {{/if}}
+    <span class="subtitle">{{subtitle}}</span>
+  {{else}}
+    {{name}}
+  {{/if}}
+</a>
diff --git a/server/sonar-web/src/main/hbs/nav/nav-search.hbs b/server/sonar-web/src/main/hbs/nav/nav-search.hbs
new file mode 100644 (file)
index 0000000..ed298ae
--- /dev/null
@@ -0,0 +1,6 @@
+<i class="navbar-search-icon icon-search"></i>
+
+<input class="navbar-search-input js-search-input" type="search" name="q" placeholder="{{t 'search_verb'}}"
+       autocomplete="off">
+
+<div class="js-search-results"></div>
index a1c209a82b6b158a0e0e78e8b1dc17768b290faa..5b15d75608fe4510e3401eb136510425d380cca7 100644 (file)
@@ -21,13 +21,13 @@ function toggleFav(resourceId, elt) {
     }});
 }
 
-function dashboardParameters() {
+function dashboardParameters (urlHasSomething) {
   var queryString = window.location.search;
-  var parameters = '';
+  var parameters = [];
 
   var matchDashboard = queryString.match(/did=\d+/);
   if (matchDashboard && $j('#is-project-dashboard').length === 1) {
-    parameters += (matchDashboard[0] + '&');
+    parameters.push(matchDashboard[0]);
   }
 
   var matchPeriod = queryString.match(/period=\d+/);
@@ -35,14 +35,15 @@ function dashboardParameters() {
     // If we have a match for period, check that it is not project-specific
     var period = parseInt(/period=(\d+)/.exec(queryString)[1]);
     if (period <= 3) {
-      parameters += matchPeriod[0] + '&';
+      parameters.push(matchPeriod[0]);
     }
   }
 
-  if (parameters !== '') {
-    parameters = '?' + parameters;
+  var query = parameters.join('&');
+  if (query !== '') {
+    query = (urlHasSomething ? '&' : '?') + query;
   }
-  return parameters;
+  return query;
 }
 
 
@@ -90,7 +91,7 @@ Treemap.prototype.load = function () {
   $j.ajax({
     type: 'GET',
     url: baseUrl + '/treemap/index?html_id=' + this.id + '&size_metric=' + this.sizeMetric +
-        '&color_metric=' + this.colorMetric + '&resource=' + context.rid,
+    '&color_metric=' + this.colorMetric + '&resource=' + context.rid,
     dataType: 'html',
     success: function (data) {
       if (data.length > 1) {
@@ -340,11 +341,7 @@ jQuery(function () {
 
   // Define global shortcuts
   key('s', function () {
-    jQuery('#searchInput').focus().on('keydown', function (e) {
-      if (e.keyCode === 27) {
-        jQuery('#searchInput').blur();
-      }
-    });
+    jQuery('.js-search-dropdown-toggle').dropdown('toggle');
     return false;
   });
 });
diff --git a/server/sonar-web/src/main/js/common/selectable-collection-view.js b/server/sonar-web/src/main/js/common/selectable-collection-view.js
new file mode 100644 (file)
index 0000000..6ddce02
--- /dev/null
@@ -0,0 +1,62 @@
+define(function () {
+
+  return Marionette.CollectionView.extend({
+
+    initialize: function () {
+      this.resetSelectedIndex();
+      this.listenTo(this.collection, 'reset', this.resetSelectedIndex);
+    },
+
+    resetSelectedIndex: function () {
+      this.selectedIndex = 0;
+    },
+
+    onRender: function () {
+      this.selectCurrent();
+    },
+
+    submitCurrent: function () {
+      var view = this.children.findByIndex(this.selectedIndex);
+      if (view != null) {
+        view.submit();
+      }
+    },
+
+    selectCurrent: function () {
+      this.selectItem(this.selectedIndex);
+    },
+
+    selectNext: function () {
+      if (this.selectedIndex < this.collection.length - 1) {
+        this.deselectItem(this.selectedIndex);
+        this.selectedIndex++;
+        this.selectItem(this.selectedIndex);
+      }
+    },
+
+    selectPrev: function () {
+      if (this.selectedIndex > 0) {
+        this.deselectItem(this.selectedIndex);
+        this.selectedIndex--;
+        this.selectItem(this.selectedIndex);
+      }
+    },
+
+    selectItem: function (index) {
+      if (index >= 0 && index < this.collection.length) {
+        var view = this.children.findByIndex(index);
+        if (view != null) {
+          view.select();
+        }
+      }
+    },
+
+    deselectItem: function (index) {
+      var view = this.children.findByIndex(index);
+      if (view != null) {
+        view.deselect();
+      }
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/nav/app.js b/server/sonar-web/src/main/js/nav/app.js
new file mode 100644 (file)
index 0000000..1f3722c
--- /dev/null
@@ -0,0 +1,34 @@
+define([
+  'nav/global-navbar-view',
+  'nav/context-navbar-view'
+], function (GlobalNavbarView, ContextNavbarView) {
+
+  var $ = jQuery,
+      App = new Marionette.Application();
+
+  App.addInitializer(function () {
+    this.navbarView = new GlobalNavbarView({
+      app: App,
+      el: $('.navbar-global'),
+      collection: new Backbone.Collection(window.navbarGlobalMenu)
+    });
+    this.navbarView.render();
+  });
+
+  if (window.navbarBreadcrumbs != null) {
+    App.addInitializer(function () {
+      this.contextNavbarView = new ContextNavbarView({
+        app: App,
+        el: $('.navbar-context'),
+        collection: new Backbone.Collection(window.navbarContextMenu),
+        breadcrumbs: new Backbone.Collection(window.navbarBreadcrumbs),
+      });
+      this.contextNavbarView.render();
+    });
+  }
+
+  window.requestMessages().done(function () {
+    App.start();
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/nav/context-navbar-view.js b/server/sonar-web/src/main/js/nav/context-navbar-view.js
new file mode 100644 (file)
index 0000000..ec6f39f
--- /dev/null
@@ -0,0 +1,17 @@
+define([
+  'templates/nav'
+], function () {
+
+  var $ = jQuery;
+
+  return Marionette.ItemView.extend({
+    template: Templates['nav-context-navbar'],
+
+    serializeData: function () {
+      return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+        breadcrumbs: this.options.breadcrumbs.toJSON()
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/nav/global-navbar-view.js b/server/sonar-web/src/main/js/nav/global-navbar-view.js
new file mode 100644 (file)
index 0000000..e87ecc6
--- /dev/null
@@ -0,0 +1,83 @@
+define([
+  'nav/search-view',
+  'templates/nav'
+], function (SearchView) {
+
+  var $ = jQuery;
+
+  return Marionette.Layout.extend({
+    template: Templates['nav-global-navbar'],
+
+    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'
+    },
+
+    initialize: function () {
+      this.projectName = window.navbarProject;
+      this.projectKey = window.navbarProjectKey;
+      this.isProjectFavorite = window.navbarProjectFavorite;
+    },
+
+    onRender: function () {
+      var that = this;
+      this.$el.addClass('navbar-' + window.navbarSpace);
+      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;
+    },
+
+    onFavoriteClick: function () {
+      var that = this,
+          p = window.process.addBackgroundProcess(),
+          url = baseUrl + '/favourites/toggle/' + window.navbarProjectId;
+      return $.post(url).done(function () {
+        that.isProjectFavorite = !that.isProjectFavorite;
+        that.render();
+        window.process.finishBackgroundProcess(p);
+      }).fail(function () {
+        window.process.failBackgroundProcess(p);
+      });
+    },
+
+    onSearchDropdownShow: function () {
+      var that = this;
+      this.searchRegion.show(new SearchView({
+        hide: function () {
+          that.$('.js-search-dropdown-toggle').dropdown('toggle');
+        }
+      }));
+    },
+
+    onSearchDropdownHidden: function () {
+      this.searchRegion.reset();
+    },
+
+    serializeData: function () {
+      return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), {
+        user: window.SS.user,
+        userName: window.SS.userName,
+        isUserAdmin: window.SS.isUserAdmin,
+
+        projectName: this.projectName,
+        projectKey: this.projectKey,
+        projectFavorite: this.isProjectFavorite,
+        navbarCanFavoriteProject: window.navbarCanFavoriteProject
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/nav/search-view.js b/server/sonar-web/src/main/js/nav/search-view.js
new file mode 100644 (file)
index 0000000..6efd0ca
--- /dev/null
@@ -0,0 +1,154 @@
+define([
+  'common/selectable-collection-view',
+  'templates/nav'
+], 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();
+        }
+      }),
+
+      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 () {
+      this.results = new Backbone.Collection();
+      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();
+      this.search(value);
+    },
+
+    onSubmit: function () {
+      return false;
+    },
+
+    resetResultsToDefault: function () {
+      var recentHistory = JSON.parse(localStorage.getItem('sonar_recent_history')),
+          history = (recentHistory || []).map(function (historyItem) {
+            return {
+              url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(historyItem.key) + dashboardParameters(true),
+              name: historyItem.name,
+              q: historyItem.icon
+            };
+          }),
+          qualifiers = window.navbarQualifiers.map(function (q) {
+            return {
+              url: baseUrl + '/all_projects?qualifier=' + encodeURIComponent(q),
+              name: t('qualifiers.all', q)
+            };
+          });
+      this.results.reset(history.concat(qualifiers));
+    },
+
+    search: function (q) {
+      if (q.length < 2) {
+        this.resetResultsToDefault();
+        return;
+      }
+      var that = this,
+          url = baseUrl + '/api/components/suggestions',
+          options = { s: q },
+          p = window.process.addBackgroundProcess();
+      return $.get(url, options).done(function (r) {
+        var collection = [];
+        r.results.forEach(function (domain) {
+          domain.items.forEach(function (item, index) {
+            var title = item.name,
+                subtitle = null;
+            if (domain.q === 'FIL' || domain.q === 'UTS') {
+              subtitle = title.substr(0, title.lastIndexOf('/') - 1);
+              title = title.substr(title.lastIndexOf('/') + 1);
+            }
+            collection.push(_.extend(item, {
+              q: domain.q,
+              title: title,
+              subtitle: subtitle,
+              extra: index === 0 ? domain.name : ' ',
+              url: baseUrl + '/dashboard/index?id=' + encodeURIComponent(item.key) + dashboardParameters(true)
+            }));
+          });
+        });
+        that.results.reset(collection);
+        window.process.finishBackgroundProcess(p);
+      }).fail(function() {
+        window.process.failBackgroundProcess(p);
+      });
+    }
+  });
+
+});
diff --git a/server/sonar-web/src/main/js/third-party/bootstrap/dropdown.js b/server/sonar-web/src/main/js/third-party/bootstrap/dropdown.js
new file mode 100644 (file)
index 0000000..69698bc
--- /dev/null
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.1
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // DROPDOWN CLASS DEFINITION
+  // =========================
+
+  var backdrop = '.dropdown-backdrop'
+  var toggle   = '[data-toggle="dropdown"]'
+  var Dropdown = function (element) {
+    $(element).on('click.bs.dropdown', this.toggle)
+  }
+
+  Dropdown.VERSION = '3.3.1'
+
+  Dropdown.prototype.toggle = function (e) {
+    var $this = $(this)
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    clearMenus()
+
+    if (!isActive) {
+      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+        // if mobile we use a backdrop because click events don't delegate
+        $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+      }
+
+      var relatedTarget = { relatedTarget: this }
+      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this
+          .trigger('focus')
+          .attr('aria-expanded', 'true')
+
+      $parent
+          .toggleClass('open')
+          .trigger('shown.bs.dropdown', relatedTarget)
+    }
+
+    return false
+  }
+
+  Dropdown.prototype.keydown = function (e) {
+    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+
+    var $this = $(this)
+
+    e.preventDefault()
+    e.stopPropagation()
+
+    if ($this.is('.disabled, :disabled')) return
+
+    var $parent  = getParent($this)
+    var isActive = $parent.hasClass('open')
+
+    if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
+      if (e.which == 27) $parent.find(toggle).trigger('focus')
+      return $this.trigger('click')
+    }
+
+    var desc = ' li:not(.divider):visible a'
+    var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+
+    if (!$items.length) return
+
+    var index = $items.index(e.target)
+
+    if (e.which == 38 && index > 0)                 index--                        // up
+    if (e.which == 40 && index < $items.length - 1) index++                        // down
+    if (!~index)                                      index = 0
+
+    $items.eq(index).trigger('focus')
+  }
+
+  function clearMenus(e) {
+    if (e && e.which === 3) return
+    $(backdrop).remove()
+    $(toggle).each(function () {
+      var $this         = $(this)
+      var $parent       = getParent($this)
+      var relatedTarget = { relatedTarget: this }
+
+      if (!$parent.hasClass('open')) return
+
+      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+
+      if (e.isDefaultPrevented()) return
+
+      $this.attr('aria-expanded', 'false')
+      $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+    })
+  }
+
+  function getParent($this) {
+    var selector = $this.attr('data-target')
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+    }
+
+    var $parent = selector && $(selector)
+
+    return $parent && $parent.length ? $parent : $this.parent()
+  }
+
+
+  // DROPDOWN PLUGIN DEFINITION
+  // ==========================
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this = $(this)
+      var data  = $this.data('bs.dropdown')
+
+      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  var old = $.fn.dropdown
+
+  $.fn.dropdown             = Plugin
+  $.fn.dropdown.Constructor = Dropdown
+
+
+  // DROPDOWN NO CONFLICT
+  // ====================
+
+  $.fn.dropdown.noConflict = function () {
+    $.fn.dropdown = old
+    return this
+  }
+
+
+  // APPLY TO STANDARD DROPDOWN ELEMENTS
+  // ===================================
+
+  $(document)
+      .on('click.bs.dropdown.data-api', clearMenus)
+      .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+      .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+      .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
+      .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
+      .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
+
+}(jQuery);
index 12c41821334ff0af9fb4bc04d19173b935af8eed..860b68b054034e79a153ee19e98b9bd95d426f82 100644 (file)
@@ -8,3 +8,5 @@
 @import "components/search-navigator";
 @import "components/typography";
 @import "components/tooltips";
+@import "components/dropdowns";
+@import "components/menu";
diff --git a/server/sonar-web/src/main/less/components/dropdowns.less b/server/sonar-web/src/main/less/components/dropdowns.less
new file mode 100644 (file)
index 0000000..756622d
--- /dev/null
@@ -0,0 +1,119 @@
+@import (reference) "../mixins";
+@import (reference) "../variables";
+@import (reference) "../ui";
+
+
+.dropdown {
+  position: relative;
+}
+
+.dropdown-toggle:focus {
+  outline: 0;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none; // none by default, but block on "open" of the menu
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  list-style: none;
+  font-size: @smallFontSize;
+  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
+  background-color: #fff;
+  border: 1px solid @barBorderColor;
+  box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+  background-clip: padding-box;
+
+  &.pull-right {
+    right: 0;
+    left: auto;
+  }
+
+  // Links within the dropdown menu
+  > li > a {
+    display: block;
+    padding: 3px 20px;
+    line-height: 1.5;
+    clear: both;
+    font-weight: normal;
+    color: @baseFontColor;
+    white-space: nowrap; // prevent links from randomly breaking onto new lines
+  }
+}
+
+// Hover/Focus state
+.dropdown-menu > li > a {
+  &:hover,
+  &:focus {
+    text-decoration: none;
+    color: @baseFontColor;
+    background-color: @lightBlue;
+  }
+}
+
+// Active state
+.dropdown-menu > .active > a {
+  &,
+  &:hover,
+  &:focus {
+    color: @baseFontColor;
+    text-decoration: none;
+    outline: 0;
+    background-color: @lightBlue;
+  }
+}
+
+// Open state for the dropdown
+.open {
+  // Show the menu
+  > .dropdown-menu {
+    display: block;
+  }
+
+  // Remove the outline when :focus is triggered
+  > a {
+    outline: 0;
+  }
+}
+
+// Menu positioning
+//
+// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown
+// menu with the parent.
+.dropdown-menu-right {
+  left: auto; // Reset the default from `.dropdown-menu`
+  right: 0;
+}
+// With v3, we enabled auto-flipping if you have a dropdown within a right
+// aligned nav component. To enable the undoing of that, we provide an override
+// to restore the default dropdown menu alignment.
+//
+// This is only for left-aligning a dropdown menu within a `.navbar-right` or
+// `.pull-right` nav component.
+.dropdown-menu-left {
+  left: 0;
+  right: auto;
+}
+
+// Dropdown section headers
+.dropdown-header {
+  display: block;
+  padding: 3px 20px;
+  font-size: @smallFontSize;
+  color: @baseFontColor;
+  white-space: nowrap; // as with > li > a
+}
+
+// Backdrop to catch body clicks on mobile, etc.
+.dropdown-backdrop {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  top: 0;
+  z-index: (1000 - 10);
+}
diff --git a/server/sonar-web/src/main/less/components/menu.less b/server/sonar-web/src/main/less/components/menu.less
new file mode 100644 (file)
index 0000000..0b78b90
--- /dev/null
@@ -0,0 +1,53 @@
+@import (reference) "../mixins";
+@import (reference) "../variables";
+@import (reference) "../ui";
+
+.menu {
+  min-width: 160px;
+  padding: 5px 0;
+  list-style: none;
+  font-size: @smallFontSize;
+  text-align: left;
+  background-color: #fff;
+  background-clip: padding-box;
+
+  > li > a,
+  > li > span {
+    display: block;
+    padding: 3px 20px;
+    line-height: 1.5;
+    clear: both;
+    font-weight: normal;
+    .text-ellipsis;
+  }
+
+  > li > a {
+    color: @baseFontColor;
+  }
+
+  .divider {
+    height: 1px;
+    margin: 9px 0;
+    overflow: hidden;
+    background-color: @barBorderColor;
+  }
+}
+
+
+.menu > li > a {
+  &:hover, &:focus {
+    text-decoration: none;
+    color: @baseFontColor;
+    background-color: @lightBlue;
+  }
+}
+
+
+.menu > .active > a {
+  &, &:hover, &:focus {
+    color: @baseFontColor;
+    text-decoration: none;
+    outline: 0;
+    background-color: @lightBlue;
+  }
+}
index f73ff56db1e913410f86f8ba7657470bdd0d130c..e5c408df6fe74bf5babdf062983b40f4fe195b99 100644 (file)
 }
 
 .search-navigator-workspace {
-  padding-left: @sideWidth;
+  padding-left: @sideWidth - 10px;
 }
 
 .search-navigator-workspace-header {
index a5bcb867a887e67b630a5a1c40edc1d1ed306a78..86dce3a54a7e981d99d6f3ca93822643569592eb 100644 (file)
@@ -15,6 +15,7 @@
 #dashboard {
   position: relative;
   width: 100%;
+  padding: 10px 0;
 }
 
 #dashboard .transparent {
index 7ccf1d683c1da4fa6f519b4ffa0437c7c51320fc..6f0d08437d962846d29800ebbaeccbefc9a48b20 100644 (file)
@@ -53,6 +53,10 @@ body, a {
 }
 
 /* LAYOUT */
+#content {
+  padding: 0 10px;
+}
+
 #container {
   height: auto !important;
   min-width: 940px;
@@ -268,7 +272,7 @@ ul.sidebar select, ul.sidebar input {
 }
 
 .page {
-  padding: 10px;
+  padding: 10px 0;
 }
 .page-split-left {
   min-width: 200px;
diff --git a/server/sonar-web/src/main/less/navbar.less b/server/sonar-web/src/main/less/navbar.less
new file mode 100644 (file)
index 0000000..c103662
--- /dev/null
@@ -0,0 +1,183 @@
+@import (reference) "mixins";
+@import (reference) "variables";
+@import (reference) "ui";
+
+@navbarGlobalBackground: #262626;
+@navbarContextBackground: @barBackgroundColor;
+
+@navbarHeight: 30px;
+@navbarLineHeight: 20px;
+@navbarTopPadding: (@navbarHeight - @navbarLineHeight) / 2;
+
+.navbar, [class^="navbar-"], [class*=" navbar-"] {
+  .box-sizing(border-box);
+}
+
+.navbar {
+  .clearfix;
+  height: @navbarHeight;
+}
+
+.navbar-fade {
+  .navbar-nav {
+    opacity: 0;
+    .trans(opacity);
+  }
+
+  .navbar-favorite {
+    margin-right: -23px;
+    .trans(margin);
+  }
+
+  &.in {
+    .navbar-nav {
+      opacity: 1;
+    }
+
+    .navbar-favorite {
+      margin-right: 0;
+    }
+  }
+}
+
+.navbar a {
+  .link-no-underline;
+}
+
+.navbar-header {
+  float: left;
+}
+
+.navbar-nav {
+  float: left;
+}
+
+.navbar-nav > li {
+  float: left;
+}
+
+.navbar-nav > li > a {
+  padding: @navbarTopPadding 10px;
+  line-height: @navbarLineHeight;
+  color: #fff;
+  font-size: 12px;
+  font-weight: 300;
+  letter-spacing: 0.05em;
+
+  &:hover, &:focus {
+    color: #fff;
+  }
+}
+
+.navbar-nav > .active > a,
+.navbar-nav > .dropdown.open > a {
+  color: #fff;
+}
+
+
+.navbar-icon:before {
+  font-size: @iconFontSize;
+}
+
+.navbar-favorite {
+  position: relative;
+  top: -2px;
+}
+
+.navbar-right {
+  float: right;
+}
+
+.navbar-search {
+  position: relative;
+  width: 400px;
+  .box-sizing(border-box);
+  .trans(width);
+}
+
+.navbar-search-input {
+  width: 100%;
+  padding: 0 20px 0 40px !important;
+  border: none !important;
+}
+
+.navbar-search-icon {
+  position: absolute;
+  top: 4px;
+  left: 20px;
+  color: @secondFontColor;
+
+  &:before {
+    font-size: @iconSmallFontSize;
+  }
+}
+
+.navbar-search-extra {
+  display: inline-block;
+  width: 90px;
+  margin-right: 10px;
+  text-align: right;
+}
+
+
+.navbar-global {
+  background-color: @navbarGlobalBackground;
+
+  .navbar-nav > li > a:hover,
+  .navbar-nav > li > a:focus,
+  .navbar-nav > .active > a,
+  .navbar-nav > .dropdown.open > a {
+    background-color: darken(@navbarGlobalBackground, 10%);
+  }
+}
+
+
+.navbar-context {
+  background-color: @navbarContextBackground;
+  border-bottom: 1px solid @barBorderColor;
+
+  .navbar-nav > li > a {
+    color: @baseFontColor;
+    font-weight: 400;
+    letter-spacing: 0;
+  }
+
+  .navbar-nav > li > a:hover,
+  .navbar-nav > li > a:focus,
+  .navbar-nav > .active > a,
+  .navbar-nav > .dropdown.open > a {
+    color: @baseFontColor;
+  }
+}
+
+.navbar-nav-pills {
+  padding-top: 4px;
+  padding-bottom: 4px;
+
+  > li + li {
+    margin-left: 5px;
+  }
+
+  > li > a {
+    padding-top: 0;
+    padding-bottom: 0;
+    border: 1px solid transparent;
+    border-radius: 3px;
+    color: @baseFontColor;
+    font-weight: 400;
+    letter-spacing: 0;
+  }
+
+  > li > a:hover,
+  > li > a:focus,
+  > .active > a,
+  > .dropdown.open > a {
+    border-color: @blue;
+    color: @baseFontColor;
+  }
+
+  > .active > a,
+  > .dropdown.open > a {
+    background-color: @lightBlue;
+  }
+}
index 5db861177683fbde83507c6db5e7f57f9d5cfd3a..d14edf4f27fe0c878c3a5d1dd26126e4bbdcf827 100644 (file)
@@ -1971,54 +1971,6 @@ table.nowrap td.small, td.nowrap.small, th.nowrap.small {
   cursor: pointer;
 }
 
-.dropdown {
-  cursor: pointer;
-  display: inline-block;
-  text-decoration: none !important;
-  /* zoom and *display = ie7 hack for display:inline-block */
-  zoom: 1;
-  *display: inline;
-}
-
-.dropdown-menu {
-  background-clip: padding-box;
-  background-color: #FFFFFF;
-  border: 1px solid #DDD;
-  box-shadow: 10px 10px 20px rgba(0, 0, 0, 0.5);
-  float: left;
-  margin: 0;
-  max-width: 220px;
-  min-width: 160px;
-  padding: 0;
-  position: absolute;
-  z-index: 1000;
-}
-
-.dropdown-menu h2 {
-  padding: 0 8px;
-  font-size: 100%;
-  font-weight: bold;
-}
-
-.dropdown-menu li {
-  list-style-type: none !important;
-  line-height: 24px;
-  height: 24px;
-  padding: 0 8px;
-  cursor: pointer !important;
-  float: none !important;
-}
-
-.dropdown-menu li:hover {
-  background-color: @lightBlue !important;
-}
-
-.dropdown-menu a {
-  text-decoration: none !important;
-  border-bottom: none !important;
-  color: @baseFontColor !important;
-}
-
 .form-key-cell {
   padding: 2px 8px 4px 10px;
   text-align: right;
index 5081ad535c1994325085d8579f029b23dcd2cdb8..f8fc1c1da669241a74218059547422382733582f 100644 (file)
@@ -7,9 +7,9 @@
   font-family: 'Roboto';
   src: url('../fonts/Roboto-Light-webfont.eot');
   src: url('../fonts/Roboto-Light-webfont.eot?#iefix') format('embedded-opentype'),
-       url('../fonts/Roboto-Light-webfont.woff') format('woff'),
-       url('../fonts/Roboto-Light-webfont.ttf') format('truetype'),
-       url('../fonts/Roboto-Light-webfont.svg#robotolight') format('svg');
+  url('../fonts/Roboto-Light-webfont.woff') format('woff'),
+  url('../fonts/Roboto-Light-webfont.ttf') format('truetype'),
+  url('../fonts/Roboto-Light-webfont.svg#robotolight') format('svg');
   font-weight: 300;
   font-style: normal;
 }
@@ -17,9 +17,9 @@
   font-family: 'Roboto';
   src: url('../fonts/Roboto-Regular-webfont.eot');
   src: url('../fonts/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
-       url('../fonts/Roboto-Regular-webfont.woff') format('woff'),
-       url('../fonts/Roboto-Regular-webfont.ttf') format('truetype'),
-       url('../fonts/Roboto-Regular-webfont.svg#robotoregular') format('svg');
+  url('../fonts/Roboto-Regular-webfont.woff') format('woff'),
+  url('../fonts/Roboto-Regular-webfont.ttf') format('truetype'),
+  url('../fonts/Roboto-Regular-webfont.svg#robotoregular') format('svg');
   font-weight: 400;
   font-style: normal;
 }
@@ -27,9 +27,9 @@
   font-family: 'Roboto';
   src: url('../fonts/Roboto-Medium-webfont.eot');
   src: url('../fonts/Roboto-Medium-webfont.eot?#iefix') format('embedded-opentype'),
-       url('../fonts/Roboto-Medium-webfont.woff') format('woff'),
-       url('../fonts/Roboto-Medium-webfont.ttf') format('truetype'),
-       url('../fonts/Roboto-Medium-webfont.svg#robotomedium') format('svg');
+  url('../fonts/Roboto-Medium-webfont.woff') format('woff'),
+  url('../fonts/Roboto-Medium-webfont.ttf') format('truetype'),
+  url('../fonts/Roboto-Medium-webfont.svg#robotomedium') format('svg');
   font-weight: 500;
   font-style: normal;
 }
@@ -37,9 +37,9 @@
   font-family: 'Roboto';
   src: url('../fonts/Roboto-Bold-webfont.eot');
   src: url('../fonts/Roboto-Bold-webfont.eot?#iefix') format('embedded-opentype'),
-       url('../fonts/Roboto-Bold-webfont.woff') format('woff'),
-       url('../fonts/Roboto-Bold-webfont.ttf') format('truetype'),
-       url('../fonts/Roboto-Bold-webfont.svg#robotobold') format('svg');
+  url('../fonts/Roboto-Bold-webfont.woff') format('woff'),
+  url('../fonts/Roboto-Bold-webfont.ttf') format('truetype'),
+  url('../fonts/Roboto-Bold-webfont.svg#robotobold') format('svg');
   font-weight: 700;
   font-style: normal;
 }
@@ -184,6 +184,7 @@ a {
 input[type=text],
 input[type=password],
 input[type=email],
+input[type=search],
 textarea {
   border: 1px solid @darkGrey;
   .box-sizing(border-box);
@@ -203,11 +204,16 @@ textarea {
 
 input[type=text],
 input[type=password],
-input[type=email] {
-  height: 22px;
+input[type=email],
+input[type=search] {
+  height: @formControlHeight;
   padding: 0 3px;
 }
 
+input[type=search] {
+  -webkit-appearance: none;
+}
+
 textarea {
   padding: 3px;
 }
@@ -218,7 +224,7 @@ input[type=submit],
 input[type=button] {
   display: inline-block;
   vertical-align: baseline;
-  height: 22px;
+  height: @formControlHeight;
   margin: 0 1px;
   padding: 0 10px;
 
@@ -264,7 +270,7 @@ input[type=button] {
   }
 }
 
-.button { line-height: 22px; }
+.button { line-height: @formControlHeight; }
 
 .button-red {
   &:hover, &:focus {
@@ -419,7 +425,7 @@ input[type=button] {
   max-width: 900px;
   margin-left: -150px;
   padding: 0 10px;
-  line-height: 22px;
+  line-height: @formControlHeight;
   background-color: #f0e8ac;
   border-radius: 0 0 3px 3px;
   text-align: center;
@@ -458,3 +464,21 @@ input[type=button] {
   font-weight: 500;
   text-align: center;
 }
+
+
+.nav {
+  margin: 0;
+  padding: 0;
+  list-style: none;
+}
+
+.nav > li {
+  position: relative;
+  display: block;
+}
+
+.nav > li > a {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+}
index 37e5e29e364519ee94eaa18364f2eb78f831b8ca..bcb64f772803cc1a2db127ecf5378918a33d7874 100644 (file)
@@ -35,6 +35,9 @@
 
 @darkBlue: darken(@blue, 20%);
 
+@darkColor: #262626;
+@lightColor: #fff;
+
 @highlighted: @blue;
 @highlightedBackground: @lightBlue;
 @contextBackground: lighten(@purple, 40%);
 @issueBorderColor: desaturate(darken(@issueBackgroundColor, 40%), 30%);
 
 
+
+/*
+ * Sizes
+ */
+
+@formControlHeight: 22px;
+
+
+
 /*
  * Icons
  */
index 410d59f601f982dec73dd635f1090520a420b213..6bc2267382f85d8166dd9d51338801530e6edfc1 100644 (file)
@@ -69,8 +69,8 @@ class Api::ComponentsController < Api::ApiController
       qualifier_results['items']=resource_indexes.map do |resource_index|
         resource=resources_by_id[resource_index.resource_id]
         {
-          'id' => resource.id,
-          'name' => resource.name(true)
+            'key' => resource.key,
+            'name' => resource.name(true)
         }
       end
       json_results<<qualifier_results
index 7bcd1ac00b4cb63a374ad1f0de91b34298aac1d5..90a27075860c8284f12e67bee6c17d6220a9050f 100644 (file)
@@ -6,7 +6,7 @@
   </style>
 <% end %>
 
-<div id="content" class="page">
+<div class="page">
   <h1 class="marginbottom10"><%= message('my_profile.page') -%></h1>
 
   <div class="admin marginbottom10">
index 78e2b2f6c8d0c3bbc8be8bedc267ee1e2e5effd1..5c77019cf4fd8aff4cc90239910bb09fd7f3a752 100644 (file)
@@ -65,7 +65,7 @@
 <% end %>
 
 
-  <div id="comparison-page">
+  <div id="comparison-page" class="page">
     <form method="GET" id="compare-form" action="<%= ApplicationController.root_context -%>/comparison/index">
       <input type="hidden" name="sids" id="sids" value="<%= @snapshots.map { |s| s.id.to_s }.join(',') -%>">
       <input type="hidden" name="metrics" id="metrics" value="<%= @metrics.map { |m| m.key }.join(',') -%>">
index adfcecbf1b6d11cd7a03d17a36c030449399f5b7..5024d0a56051db28d0913100a39de3011d1c7e27 100644 (file)
@@ -25,76 +25,78 @@ padding: 5px;
 }
 </style>
 <% end %>
-<form action="<%= ApplicationController.root_context -%>/dependencies/index" id="search_form">
-  <input type="text" name="search" value="<%= h params[:search] -%>" id="search_input"> </input>
-  <input type="submit" value="<%= message('dependencies.search_library') -%>" id="search_submit"/><br/>
-  <p class="small gray"><%= message('dependencies.search_help') -%></p>
-</form>
+<div class="page">
+  <form action="<%= ApplicationController.root_context -%>/dependencies/index" id="search_form">
+    <input type="text" name="search" value="<%= h params[:search] -%>" id="search_input"> </input>
+    <input type="submit" value="<%= message('dependencies.search_library') -%>" id="search_submit"/><br/>
+    <p class="small gray"><%= message('dependencies.search_help') -%></p>
+  </form>
 
-<div id="deps_drilldown">
-  <% if @resources %>
-  <div id="artifacts_col" class="drilldown_col">
-    <h3><%= message('dependencies.select_library') -%> :</h3>
-    <div class="col">
-    <table>
-      <tbody>
-      <% if @resources.empty? %>
-      <tr class="even"><td><%= message('no_data') -%></td></tr>
-      <% end %>
-     <% @resources.each do |resource|%>
-      <tr class="<%= cycle('even', 'odd', :name => 'lib') -%> <%= 'selected' if resource==@resource -%>">
-        <td ><%= qualifier_icon(resource) %> <a href="<%= url_for :action => 'index', :overwrite_params => {:version => nil, :resource => resource.kee} -%>"><%= h resource.name(true) -%></a><br/><span class="small gray"><%= h resource.kee -%></span></td>
-      </tr>
-      <% end %>
-      </tbody>
-    </table>
+  <div id="deps_drilldown">
+    <% if @resources %>
+    <div id="artifacts_col" class="drilldown_col">
+      <h3><%= message('dependencies.select_library') -%> :</h3>
+      <div class="col">
+      <table>
+        <tbody>
+        <% if @resources.empty? %>
+        <tr class="even"><td><%= message('no_data') -%></td></tr>
+        <% end %>
+       <% @resources.each do |resource|%>
+        <tr class="<%= cycle('even', 'odd', :name => 'lib') -%> <%= 'selected' if resource==@resource -%>">
+          <td ><%= qualifier_icon(resource) %> <a href="<%= url_for :action => 'index', :overwrite_params => {:version => nil, :resource => resource.kee} -%>"><%= h resource.name(true) -%></a><br/><span class="small gray"><%= h resource.kee -%></span></td>
+        </tr>
+        <% end %>
+        </tbody>
+      </table>
+      </div>
     </div>
-  </div>
-  <% end %>
+    <% end %>
 
-  <% if @versions %>
-  <div id="versions_col" class="drilldown_col">
-    <h3><%= message('dependencies.select_version') -%> :</h3>
-    <div class="col">
-    <table>
-      <tbody>
-      <% if @versions.size>1 %>
-        <tr class="<%= cycle('even', 'odd', :name => 'version') -%> <%= 'selected' if @version.blank? -%>">
-          <td><a href="<%= url_for :action => 'index', :overwrite_params => {:version => nil, :resource => @resource.kee} -%>">All</a></td>
+    <% if @versions %>
+    <div id="versions_col" class="drilldown_col">
+      <h3><%= message('dependencies.select_version') -%> :</h3>
+      <div class="col">
+      <table>
+        <tbody>
+        <% if @versions.size>1 %>
+          <tr class="<%= cycle('even', 'odd', :name => 'version') -%> <%= 'selected' if @version.blank? -%>">
+            <td><a href="<%= url_for :action => 'index', :overwrite_params => {:version => nil, :resource => @resource.kee} -%>">All</a></td>
+          </tr>
+        <% end %>
+       <% @versions.each do |version|%>
+        <tr class="<%= cycle('even', 'odd', :name => 'version') -%> <%= 'selected' if version==@version-%>">
+          <td><a href="<%= url_for :action => 'index', :overwrite_params => {:version => version, :resource => @resource.kee} -%>"><%= version -%></a></td>
         </tr>
-      <% end %>
-     <% @versions.each do |version|%>
-      <tr class="<%= cycle('even', 'odd', :name => 'version') -%> <%= 'selected' if version==@version-%>">
-        <td><a href="<%= url_for :action => 'index', :overwrite_params => {:version => version, :resource => @resource.kee} -%>"><%= version -%></a></td>
-      </tr>
-      <% end %>
-      </tbody>
-    </table>
+        <% end %>
+        </tbody>
+      </table>
+      </div>
     </div>
-  </div>
-  <% end %>
+    <% end %>
 
-  <% if @project_snapshots %>
-  <div id="results_col" class="drilldown_col">
-    <h3><%= message('dependencies.used_by') -%> :</h3>
-    <div class="col">
-    <table>
-      <tbody>
-      <% if @project_snapshots.empty? %>
-      <tr class="even"><td><%= message('dependencies.not_used') -%></td></tr>
-      <% end %>
-     <% @project_snapshots.each do |project_snapshot|%>
-      <tr class="<%= cycle('even', 'odd', :name => 'dep') -%>">
-        <td>
-        <%= qualifier_icon(project_snapshot.project) %> <%= link_to h(project_snapshot.project.name(true)), "#{ApplicationController.root_context}/libraries/index/#{project_snapshot.project_id}" -%><br/>
-        <span class="small gray"><%= h project_snapshot.project.kee -%></span></td>
-      </tr>
-      <% end %>
-      </tbody>
-    </table>
+    <% if @project_snapshots %>
+    <div id="results_col" class="drilldown_col">
+      <h3><%= message('dependencies.used_by') -%> :</h3>
+      <div class="col">
+      <table>
+        <tbody>
+        <% if @project_snapshots.empty? %>
+        <tr class="even"><td><%= message('dependencies.not_used') -%></td></tr>
+        <% end %>
+       <% @project_snapshots.each do |project_snapshot|%>
+        <tr class="<%= cycle('even', 'odd', :name => 'dep') -%>">
+          <td>
+          <%= qualifier_icon(project_snapshot.project) %> <%= link_to h(project_snapshot.project.name(true)), "#{ApplicationController.root_context}/libraries/index/#{project_snapshot.project_id}" -%><br/>
+          <span class="small gray"><%= h project_snapshot.project.kee -%></span></td>
+        </tr>
+        <% end %>
+        </tbody>
+      </table>
+      </div>
     </div>
+    <% end %>
   </div>
-  <% end %>
 </div>
 <script>
 $j('#artifacts_col tr.selected').each(function(index,item) {item.scrollIntoView(true);});
index 7b8d6f680e86709611f9ac8aef4a0f74c49f7e95..453350a9c22e162f43b640ade721fe8362508acf 100644 (file)
@@ -3,108 +3,8 @@
    display_only_root = @issue
 %>
 
-<% if @breadcrumbs %>
+<% if displayed_resource %>
 
-  <div id="crumbs">
-    <ul id="crumbs-ops">
-      <% if displayed_resource && !display_only_root %>
-        <li>
-          <%= link_to_favourite(displayed_resource) -%>
-        </li>
-        <% if has_role?(:admin, displayed_resource) && !displayed_resource.entity? %>
-          <%= render 'layouts/menu_resource_settings' -%>
-        <% end %>
-      <% end %>
-    </ul>
-    <ul id="bc">
-      <%
-         @breadcrumbs.each do |breadcrumb|
-      %>
-        <li>
-          <% if breadcrumb.is_a?(String) %>
-            <span><%= h breadcrumb -%></span>
-          <%
-             elsif breadcrumb.is_a?(Hash)
-               name = breadcrumb[:name]
-               url = breadcrumb[:url]
-               icon = breadcrumb[:icon]
-          %>
-            <% if icon %><img src="<%= ApplicationController.root_context -%>/images/<%= icon -%>" width="16" height="16"><% end %>
-            <%= link_to_if url, h(name), url -%>
-          <% elsif breadcrumb.respond_to?(:html) %>
-            <%= breadcrumb.html -%>
-          <% end %>
-        </li>
-      <% end %>
-    </ul>
-  </div>
-
-<% elsif displayed_resource %>
-
-  <div id="crumbs">
-    <ul id="crumbs-ops">
-      <% if displayed_resource && !display_only_root %>
-        <li>
-          <%= link_to_favourite(displayed_resource) -%>
-        </li>
-        <% if (has_role?(:admin, displayed_resource) || profiles_administrator?) && !displayed_resource.entity? %>
-          <%= render 'layouts/menu_resource_settings' -%>
-        <% end %>
-      <% end %>
-    </ul>
-    <ul id="bc">
-      <%
-         resource_link = {:controller => params[:controller], :action => params[:action]}
-         if display_only_root
-           resource_link = {:controller => 'dashboard', :action => 'index'}
-         end
-
-         # ======== Path for resources ========
-
-         if displayed_resource
-           if displayed_resource.last_snapshot
-             resources=[]
-             s=displayed_resource.last_snapshot
-             while (s!=nil) do
-               resources.insert(0, s.project)
-               s=s.parent
-             end
-           else
-             resources = [displayed_resource]
-           end
-      %>
-        <%
-           resources.each do |resource|
-        %>
-          <li>
-            <%= qualifier_icon(resource) -%>
-            &nbsp;
-            <%=
-                root = "#{ApplicationController.root_context}/#{u resource_link[:controller]}/#{u resource_link[:action]}?"
-                path = ''
-                query = request.query_parameters
-                query[:id] = resource.key
-                query.each do |key, value|
-                  path += '&' unless path.empty?
-                  path += "#{u key}=#{u value}"
-                end
-                "<a href='#{root + path}'>#{h(resource.name)}</a>"
-            -%>
-          </li>
-        <%
-           end
-           end
-
-         # ======== Path for issue ========
-         if @issue
-      %>
-        <li>
-          <%= link_to message('issue') + ' #' + @issue.key.to_s -%>
-        </li>
-      <%
-         end
-      %>
-    </ul>
-  </div>
+  <nav class="navbar navbar-breadcrumbs"></nav>
 
 <% end %>
index fb83ec46c995e3913c57f5d2916f0304c1f90e80..5705ddc48832c1a47888bd711e4eb8d1db5ec4f5 100644 (file)
    period_param = "period=#{u(params[:period])}" if params[:period]
 %>
 <div id="container">
-  <%= yield :header -%>
-  <div id="hd">
-    <div id="nav-left">
-      <ul>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/" class="<%= 'selected' if selected_section==Navigation::SECTION_HOME || selected_section==Navigation::SECTION_RESOURCE -%>"><%= message('layout.dashboards') -%></a>
-        </li>
-        <%= render 'layouts/menu_projects' -%>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/measures/search?qualifiers[]=TRK" class="<%= 'selected' if selected_section==Navigation::SECTION_MEASURES -%>"><%= message('layout.measures') -%></a>
-        </li>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/issues/index" class="<%= 'selected' if selected_section==Navigation::SECTION_ISSUES -%>"><%= message('issues.page') -%></a>
-        </li>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/coding_rules" class="<%= 'selected' if selected_section==Navigation::SECTION_CODING_RULES -%>"><%= message('coding_rules.page') -%></a>
-        </li>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/profiles" class="<%= 'selected' if selected_section==Navigation::SECTION_QUALITY_PROFILES -%>"><%= message('quality_profiles.page') -%></a>
-        </li>
-        <li>
-          <a href="<%= ApplicationController.root_context -%>/quality_gates" class="<%= 'selected' if selected_section==Navigation::SECTION_QUALITY_GATES -%>"><%= message('quality_gates.page') -%></a>
-        </li>
-      </ul>
-    </div>
-    <div id="nav">
-      <ul>
-        <li>
-          <input type="text" size="15" name="search" id="searchInput" value="<%= message('search_verb') -%>">
-          <img src="<%= ApplicationController.root_context -%>/images/loading-small.gif" id="searchingResources" style="display:none">
-        </li>
-        <% if logged_in? %>
-          <%= render 'layouts/menu_user' -%>
-        <% else %>
-          <li><a id="login-link" href="<%= ApplicationController.root_context -%>/sessions/new?return_to=<%= u (request.request_uri) -%>"><%= message('layout.login') -%></a></li>
-        <% end %>
-        <% if is_admin? %>
-          <li><a href="<%= ApplicationController.root_context -%>/settings/index" class="<%= 'selected' if selected_section==Navigation::SECTION_CONFIGURATION -%>"><%= message('layout.settings') -%></a></li>
-        <% end %>
-      </ul>
-    </div>
-    <div id="searchResourcesResults" class="autocomplete" style="display:none"
-         data-results="<%= message('search.results') -%>"
-         data-no-results="<%= message('no_results') -%>"></div>
-  </div>
-  <%= render 'layouts/breadcrumb' if @breadcrumbs || selected_section==Navigation::SECTION_RESOURCE -%>
-  <div id="body">
-    <% if selected_section && selected_section.show_sidebar && !@hide_sidebar %>
-      <div id="sidebar">
-        <ul class="sidebar blue-sidebar">
-          <% if selected_section==Navigation::SECTION_HOME %>
-            <% ActiveDashboard.user_dashboards(current_user, true).each do |active_dashboard| %>
-              <li class="<%= 'active' if @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id -%>">
-                <a href="<%= ApplicationController.root_context -%>/dashboard/?did=<%= active_dashboard.dashboard_id -%>"><%= h active_dashboard.dashboard.name(true) -%></a>
-              </li>
-            <% end %>
-
-            <% controller.java_facade.getPages(Navigation::SECTION_HOME.key, nil, nil, nil, nil).each do |page|
-              page_url = (page.isController() ? page.getId() : "/plugins/home/#{page.getId()}")
-              selected=request.request_uri.include?("/plugins/home/#{page_url}")
-            %>
-              <li class="<%= 'active' if selected -%>">
-                <a href="<%= ApplicationController.root_context -%><%= page_url -%>"><%= h message(page.getId() + '.page', :default => page.getTitle()) -%></a></li>
-            <% end %>
-
-            <li class="spacer"></li>
-            <li class="sidebar-title"><%= message('sidebar.tools') -%></li>
-
-            <li class="<%= 'active' if controller.controller_path=='dependencies' -%>">
-              <a href="<%= ApplicationController.root_context -%>/dependencies/index"><%= message('dependencies.page') -%></a></li>
-            <li class="<%= 'active' if request.request_uri.include?('/comparison/index') -%>">
-              <a href="<%= ApplicationController.root_context -%>/comparison/index"><%= message('comparison.page') -%></a>
-            </li>
-
-          <% elsif selected_section==Navigation::SECTION_RESOURCE %>
-            <% ActiveDashboard.user_dashboards(current_user, false).each do |active_dashboard| %>
-              <li class="<%= 'active' if @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id -%>">
-                <a href="<%= ApplicationController.root_context -%>/dashboard/index/<%= @project.id -%>?did=<%= active_dashboard.dashboard_id -%><%= "&"+period_param if period_param -%>"><%= h active_dashboard.dashboard.name(true) -%></a>
-              </li>
-            <% end %>
+  <nav class="navbar navbar-global"></nav>
 
-            <% if @snapshot %>
-              <li class="spacer"></li>
-              <li class="sidebar-title"><%= message('sidebar.tools') -%></li>
-              <li class="<%= 'active' if request.request_uri.include?('/components/index') -%>">
-                <a href="<%= ApplicationController.root_context -%>/components/index/<%= @project.id -%>"><%= message('components.page') -%></a>
-              </li>
-              <li class="<%= 'active' if request.request_uri.include?('/drilldown/issues') -%>">
-                <a href="<%= ApplicationController.root_context -%>/drilldown/issues/<%= @project.id -%><%= "?"+period_param if period_param -%>"><%= message('issues_drilldown.page') -%></a>
-              </li>
-              <% project_metrics = @project.last_snapshot.metric_keys.to_java(:string) if @project.last_snapshot
-                 controller.java_facade.getPages(Navigation::SECTION_RESOURCE.key, @project.scope, @project.qualifier, @project.language, project_metrics).each do |page|
-                   page_url = (page.isController() ? "#{page.getId()}?id=#{@project.id}" : "/plugins/resource/#{@project.id}?page=#{page.getId()}")
-              %>
-                  <li class="<%= 'active' if request.request_uri.include?(page_url) -%>">
-                    <a href="<%= ApplicationController.root_context -%><%= page_url -%>"><%= h message(page.getId() + '.page', :default => page.getTitle()) -%></a>
-                  </li>
-              <% end %>
-              <li class="<%= 'active' if request.request_uri.include?('/design/index') -%>">
-                <a href="<%= ApplicationController.root_context -%>/design/index/<%= @project.key -%>"><%= message('design.page') -%></a>
-              </li>
-              <li class="<%= 'active' if request.request_uri.include?('/libraries/index') -%>">
-                <a href="<%= ApplicationController.root_context -%>/libraries/index/<%= @project.key -%>"><%= message('libraries.page') -%></a>
-              </li>
-              <% if controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'comparable') %>
-              <li class="<%= 'active' if request.request_uri.include?('/comparison/index') -%>">
-                <a href="<%= ApplicationController.root_context -%>/comparison/index?resource=<%= @project.key -%>"><%= message('comparison.page') -%></a>
-              </li>
-              <% end %>
-            <% end %>
+  <% if selected_section==Navigation::SECTION_RESOURCE %>
+    <nav class="navbar navbar-context"></nav>
+  <% end %>
 
-          <% elsif selected_section==Navigation::SECTION_CONFIGURATION %>
-
-            <% if is_admin? %>
-
-            <li class="sidebar-title"><%= message('sidebar.project_settings') -%></li>
-            <li class="<%= 'active' if request.request_uri.include?('/settings') -%>">
-              <a href="<%= ApplicationController.root_context -%>/settings/index"><%= message('settings.page') -%></a></li>
-            <li class="<%= 'active' if controller.controller_path=='metrics' -%>">
-              <a href="<%= ApplicationController.root_context -%>/metrics/index"><%= message('manual_metrics.page') -%></a></li>
-            <li class="<%= 'active' if controller.controller_path=='admin_dashboards' -%>">
-              <a href="<%= ApplicationController.root_context -%>/admin_dashboards/index"><%= message('default_dashboards.page') -%></a></li>
-            <% controller.java_facade.getPages(Navigation::SECTION_CONFIGURATION.key, nil, nil, nil, nil).each do |page|
-              page_url = (page.isController() ? page.getId() : "/plugins/configuration/#{page.getId()}")
-            %>
-              <li class="<%= 'active' if request.request_uri.include?(page_url) -%>">
-                <a href="<%= ApplicationController.root_context -%><%= page_url -%>"><%= h message(page.getId() + '.page', :default => page.getTitle()) %></a>
-              </li>
-            <% end %>
-            <li class="spacer"></li>
-            <li class="sidebar-title"><%= message('sidebar.security') -%></li>
-            <li class="<%= 'active' if request.request_uri.include?('/users') -%>"><a href="<%= ApplicationController.root_context -%>/users"><%= message('users.page') -%></a>
-            </li>
-            <li class="<%= 'active' if request.request_uri.include?('/groups') -%>">
-              <a href="<%= ApplicationController.root_context -%>/groups/index"><%= message('user_groups.page') -%></a></li>
-            <li class="<%= 'active' if request.request_uri.include?('/roles/global') -%>">
-              <a href="<%= ApplicationController.root_context -%>/roles/global"><%= message('global_permissions.page') -%></a></li>
-            <li class="<%= 'active' if request.request_uri.include?('/roles/projects') || request.request_uri.include?('/permission_templates') -%>">
-              <a href="<%= ApplicationController.root_context -%>/roles/projects"><%= message('roles.page') -%></a></li>
-
-            <li class="spacer"></li>
-            <li class="sidebar-title"><%= message('sidebar.system') -%></li>
-            <% if has_role?("provisioning") %>
-            <li class="<%= 'active' if controller.controller_path=='provisioning' -%>">
-              <a href="<%= ApplicationController.root_context -%>/provisioning"><%= message('provisioning.page') -%></a>
-            </li>
-            <% end %>
-            <li class="<%= 'active' if controller.controller_path=='bulk_deletion' -%>">
-              <a href="<%= ApplicationController.root_context -%>/bulk_deletion"><%= message('bulk_deletion.page') -%></a>
-            </li>
-            <% update_center_activated = controller.java_facade.getSettings().getBoolean('sonar.updatecenter.activate')
-               if update_center_activated %>
-              <li class="<%= 'active' if controller.controller_path=='updatecenter' -%>">
-                <a href="<%= ApplicationController.root_context -%>/updatecenter"><%= message('update_center.page') -%></a></li>
-            <% end %>
-            <li class="<%= 'active' if controller.controller_path=='system' -%>">
-              <a href="<%= ApplicationController.root_context -%>/system"><%= message('system_info.page') -%></a></li>
-            <li class="<%= 'active' if controller.controller_path=='analysis_reports' -%>">
-              <a href="<%= ApplicationController.root_context -%>/analysis_reports"><%= message('analysis_reports.page') -%></a></li>
-
-            <% end #of admin part %>
-
-          <% end %>
-
-          <%= yield :sidebar %>
+  <%= yield :header -%>
 
-          <li id="logo">
-            <center><a href="http://www.sonarqube.org/" target="SonarSource"><%= image_tag('logo.png', :alt => message('layout.sonar.slogan'), :width => 100, :height => 24) -%></a></center>
-          </li>
-        </ul>
-      </div>
-    <% end %>
-    <%
-      html_id_class = ''
-      html_id_class = "id='content' class='with_sidebar'" if selected_section && selected_section.show_sidebar
-      html_id_class = "id='content'" if @hide_sidebar || (selected_section && !selected_section.show_sidebar)
-    %>
-    <div <%= html_id_class -%>>
+  <div id="body">
+    <div id="content">
       <% if @project %>
         <div class="print"><h2><%= h @project.name() %></h2></div>
       <% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar.html.erb
new file mode 100644 (file)
index 0000000..fd8d74e
--- /dev/null
@@ -0,0 +1,5 @@
+<%= render 'layouts/navbar_conf' -%>
+<%= render 'layouts/recent_history' -%>
+<script>
+  require(['nav/app']);
+</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf.html.erb
new file mode 100644 (file)
index 0000000..98319a4
--- /dev/null
@@ -0,0 +1,29 @@
+<%
+   selected_section = controller.class::SECTION if defined?(controller.class::SECTION)
+   if selected_section==Navigation::SECTION_RESOURCE && !@project && !@resource
+     selected_section = Navigation::SECTION_HOME
+   end
+   @project=@resource unless @project || selected_section==Navigation::SECTION_HOME
+   period_param = "period=#{u(params[:period])}" if params[:period]
+   displayed_resource = @resource || @project
+   display_only_root = @issue
+   resource_id=displayed_resource ? (displayed_resource.is_a?(Fixnum) ? displayed_resource : displayed_resource.permanent_id) : nil
+%>
+
+<%= render :partial => 'layouts/navbar_conf_global' %>
+
+<% if selected_section==Navigation::SECTION_RESOURCE %>
+  <%= render :partial => 'layouts/navbar_conf_context' %>
+<% elsif selected_section==Navigation::SECTION_CONFIGURATION %>
+  <%= render :partial => 'layouts/navbar_conf_settings' %>
+<% end %>
+
+
+<script>
+  var navbarQualifiers = [
+    <% Project.root_qualifiers.sort.each do |qualifier| %>
+    '<%= qualifier -%>',
+    <% end %>
+  ];
+  window.SS.isUserAdmin = <%= current_user && is_admin? ? 'true' : 'false' -%>;
+</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_context.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_context.html.erb
new file mode 100644 (file)
index 0000000..58d7c40
--- /dev/null
@@ -0,0 +1,208 @@
+<%
+   @project=@resource unless @project || selected_section==Navigation::SECTION_HOME
+   period_param = "period=#{u(params[:period])}" if params[:period]
+   displayed_resource = @resource || @project
+   display_only_root = @issue
+   resource_id=displayed_resource ? (displayed_resource.is_a?(Fixnum) ? displayed_resource : displayed_resource.permanent_id) : nil
+%>
+
+
+<script>
+  var navbarProject = '<%= @project.root_project().name -%>',
+      navbarProjectId = '<%= @project.root_project().id -%>',
+      navbarProjectKey = '<%= @project.root_project().key -%>',
+      navbarCanFavoriteProject = <%= logged_in? && displayed_resource && !display_only_root ? 'true' : 'false' -%>,
+      navbarProjectFavorite = <%= logged_in? && current_user.favourite?(resource_id) ? 'true' : 'false' -%>,
+      navbarContextMenu = [
+        {
+          label: 'layout.dashboards',
+          active: <%= @dashboard && controller.controller_path=='dashboard' ? "true" : "false" -%>,
+          menu: [
+            <% ActiveDashboard.user_dashboards(current_user, false).each do |active_dashboard| %>
+            {
+              url: '/dashboard/index?id=<%= @project.key -%>&did=<%= active_dashboard.dashboard_id -%><%= "&"+period_param if period_param -%>',
+              labelLocalized: '<%= escape_javascript active_dashboard.dashboard.name(true) -%>',
+              active: <%= @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id ? "true" : "false" -%>
+            },
+            <% end %>
+          ]
+        },
+        <% if @snapshot %>
+        {
+          url: '/components/index/<%= @project.id -%>',
+          label: 'components.page',
+          active: <%= request.request_uri.include?('/components/index') ? "true" : "false" -%>
+        },
+        {
+          url: '/drilldown/issues/<%= @project.id -%><%= "?"+period_param if period_param -%>',
+          label: 'issues.page',
+          active: <%= request.request_uri.include?('/drilldown/issues') ? "true" : "false" -%>
+        },
+        <% if (has_role?(:admin, displayed_resource) || profiles_administrator?) && !displayed_resource.entity? %>
+        <% is_admin = has_role?(:admin, @project) %>
+        {
+          labelLocalized: '<%= message('qualifier.configuration.' + @project.qualifier) -%>',
+          active: false,
+          menu: [
+            <% if (is_admin && controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'configurable')) %>
+            {
+              url: '/project/settings/<%= @project.id -%>',
+              label: 'project_settings.page',
+              active: false
+            },
+            <% end %>
+            <% if (@project.project?) %>
+            {
+              url: '/project/profile/<%= @project.id -%>',
+              label: 'project_quality_profiles.page',
+              active: false
+            },
+            <% end %>
+            <% if (@project.project?) %>
+            {
+              url: '/project/qualitygate/<%= @project.id -%>',
+              label: 'project_quality_gate.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin %>
+            {
+              url: '/manual_measures/index/<%= @project.id -%>',
+              label: 'manual_measures.page',
+              active: false
+            },
+            <% end %>
+            <% if (is_admin && @project.project?) %>
+            {
+              url: '/action_plans/index/<%= @project.id -%>',
+              label: 'action_plans.page',
+              active: false
+            },
+            <% end %>
+            <% if (is_admin && @project.project?) %>
+            {
+              url: '/project/links/<%= @project.id -%>',
+              label: 'project_links.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin && controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'hasRolePolicy') %>
+            {
+              url: '/project_roles/index/<%= @project.id -%>',
+              label: 'permissions.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin && controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'modifiable_history') %>
+            {
+              url: '/project/history/<%= @project.id -%>',
+              label: 'project_history.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin && controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'updatable_key') %>
+            {
+              url: '/project/key/<%= @project.id -%>',
+              label: 'update_key.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin && controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'deletable') %>
+            {
+              url: '/project/deletion/<%= @project.id -%>',
+              label: 'deletion.page',
+              active: false
+            },
+            <% end %>
+            <% if is_admin
+                controller.java_facade.getPages(Navigation::SECTION_RESOURCE_CONFIGURATION.key, @project.scope, @project.qualifier, @project.language, nil).each do |page|
+                  page_url = "#{page.getId()}?resource=#{@project.id}"
+            %>
+            {
+              url: '<%= page_url -%>',
+              labelLocalized: '<%= escape_javascript message(page.getId() + '.page', :default => page.getTitle()) -%>',
+              active: false
+            },
+            <%   end
+               end %>
+          ]
+        },
+        <% end %>
+        <% end %>
+        {
+          label: 'more',
+          active: false,
+          menu: [
+            <% project_metrics = @project.last_snapshot.metric_keys.to_java(:string) if @project.last_snapshot
+               controller.java_facade.getPages(Navigation::SECTION_RESOURCE.key, @project.scope, @project.qualifier, @project.language, project_metrics).each do |page|
+                 page_url = (page.isController() ? "#{page.getId()}?id=#{@project.id}" : "/plugins/resource/#{@project.id}?page=#{page.getId()}")
+            %>
+            {
+              url: '<%= page_url -%>',
+              labelLocalized: '<%= escape_javascript message(page.getId() + '.page', :default => page.getTitle()) -%>',
+              active: <%= request.request_uri.include?(page_url) ? "true" : "false" -%>
+            },
+            <% end %>
+            {
+              url: '/design/index/<%= @project.key -%>',
+              label: 'design.page',
+              active: <%= request.request_uri.include?('/design/index') ? "true" : "false" -%>
+            },
+            {
+              url: '/libraries/index/<%= @project.key -%>',
+              label: 'libraries.page',
+              active: <%= request.request_uri.include?('/libraries/index') ? "true" : "false" -%>
+            },
+            <% if controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'comparable') %>
+            {
+              url: '/comparison/index?resource=<%= @project.key -%>',
+              label: 'comparison.page',
+              active: <%= request.request_uri.include?('/comparison/index') ? "true" : "false" -%>
+            }
+            <% end %>
+          ]
+        }
+      ],
+      navbarBreadcrumbs = [
+        <%
+         resource_link = {:controller => params[:controller], :action => params[:action]}
+         if display_only_root
+           resource_link = {:controller => 'dashboard', :action => 'index'}
+         end
+
+         # ======== Path for resources ========
+
+         if displayed_resource
+           if displayed_resource.last_snapshot
+             resources=[]
+             s=displayed_resource.last_snapshot
+             while (s!=nil) do
+               resources.insert(0, s.project)
+               s=s.parent
+             end
+           else
+             resources = [displayed_resource]
+           end
+      %>
+        <%
+           resources.each do |resource|
+           root = "/#{u resource_link[:controller]}/#{u resource_link[:action]}?"
+    path = ''
+    query = request.query_parameters
+    query[:id] = resource.key
+    query.each do |key, value|
+      path += '&' unless path.empty?
+      path += "#{u key}=#{u value}"
+    end
+        %>
+        {
+          q: '<%= resource.qualifier -%>',
+          url: '<%= root + path -%>',
+          name: '<%= escape_javascript resource.name -%>'
+        },
+        <%
+           end
+           end
+        %>
+      ];
+</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_global.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_global.html.erb
new file mode 100644 (file)
index 0000000..9222f66
--- /dev/null
@@ -0,0 +1,75 @@
+<%
+   selected_section = controller.class::SECTION if defined?(controller.class::SECTION)
+   if selected_section==Navigation::SECTION_RESOURCE && !@project && !@resource
+     selected_section = Navigation::SECTION_HOME
+   end
+%>
+
+<script>
+  window.navbarGlobalMenu = [
+    {
+      label: 'layout.dashboards',
+      active: <%= selected_section==Navigation::SECTION_HOME ? 'true' : 'false' -%>,
+      menu: [
+        <% ActiveDashboard.user_dashboards(current_user, true).each do |active_dashboard| %>
+        {
+          url: '/dashboard/?did=<%= active_dashboard.dashboard_id -%>',
+          labelLocalized: '<%= escape_javascript active_dashboard.dashboard.name(true) -%>',
+          active: <%= @dashboard && controller.controller_path=='dashboard' && active_dashboard.dashboard_id==@dashboard.id ? 'true' : 'false' -%>
+        },
+        <% end %>
+      ]
+    },
+    {
+      url: '/measures/search?qualifiers[]=TRK',
+      label: 'layout.measures',
+      active: <%= selected_section==Navigation::SECTION_MEASURES ? 'true' : 'false' -%>
+    },
+    {
+      url: '/issues/index',
+      label: 'issues.page',
+      active: <%= selected_section==Navigation::SECTION_ISSUES ? 'true' : 'false' -%>
+    },
+    {
+      url: '/coding_rules',
+      label: 'coding_rules.page',
+      active: <%= selected_section==Navigation::SECTION_CODING_RULES ? 'true' : 'false' -%>
+    },
+    {
+      url: '/profiles',
+      label: 'quality_profiles.page',
+      active: <%= selected_section==Navigation::SECTION_QUALITY_PROFILES ? 'true' : 'false' -%>
+    },
+    {
+      url: '/quality_gates',
+      label: 'quality_gates.page',
+      active: <%= selected_section==Navigation::SECTION_QUALITY_GATES ? 'true' : 'false' -%>
+    },
+    {
+      label: 'more',
+      active: false,
+      menu: [
+        {
+          url: '/comparison/index',
+          label: 'comparison.page',
+          active: <%= request.request_uri.include?('/comparison') ? 'true' : 'false' -%>
+        },
+        {
+          url: '/dependencies/index',
+          label: 'dependencies.page',
+          active: <%= request.request_uri.include?('/dependencies') ? 'true' : 'false' -%>
+        },
+        <% controller.java_facade.getPages(Navigation::SECTION_HOME.key, nil, nil, nil, nil).each do |page|
+          page_url = (page.isController() ? page.getId() : "/plugins/home/#{page.getId()}")
+          selected=request.request_uri.include?("/plugins/home/#{page_url}")
+        %>
+        {
+          url: '<%= page_url -%>',
+          labelLocalized: '<%= escape_javascript message(page.getId() + '.page', :default => page.getTitle()) -%>',
+          active: <%= selected ? 'true' : 'false' -%>
+        },
+        <% end %>
+      ]
+    }
+  ];
+</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_settings.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_navbar_conf_settings.html.erb
new file mode 100644 (file)
index 0000000..e6268a2
--- /dev/null
@@ -0,0 +1,109 @@
+<%
+   selected_section = controller.class::SECTION if defined?(controller.class::SECTION)
+   if selected_section==Navigation::SECTION_RESOURCE && !@project && !@resource
+     selected_section = Navigation::SECTION_HOME
+   end
+%>
+
+<script>
+  var navbarSpace = 'settings',
+      navbarGlobalMenu = [
+        {
+          label: 'sidebar.project_settings',
+          active: false,
+          menu: [
+            {
+              url: '/settings/index',
+              label: 'settings.page',
+              active: <%= request.request_uri.include?('/settings') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/metrics/index',
+              label: 'manual_metrics.page',
+              active: <%= request.request_uri.include?('/metrics') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/admin_dashboards/index',
+              label: 'default_dashboards.page',
+              active: <%= request.request_uri.include?('/admin_dashboards') ? 'true' : 'false' -%>
+            },
+            <% controller.java_facade.getPages(Navigation::SECTION_CONFIGURATION.key, nil, nil, nil, nil).each do |page|
+                        page_url = (page.isController() ? page.getId() : "/plugins/configuration/#{page.getId()}")
+                      %>
+            {
+              url: '<%= page_url -%>',
+              labelLocalized: '<%= escape_javascript message(page.getId() + '.page', :default => page.getTitle()) %>',
+              active: <%= request.request_uri.include?(page_url) ? 'true' : 'false' -%>
+            },
+            <% end %>
+          ]
+        },
+        {
+          label: 'sidebar.security',
+          active: false,
+          menu: [
+            {
+              url: '/users/index',
+              label: 'users.page',
+              active: <%= request.request_uri.include?('/users') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/groups/index',
+              label: 'user_groups.page',
+              active: <%= request.request_uri.include?('/groups') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/roles/global',
+              label: 'global_permissions.page',
+              active: <%= request.request_uri.include?('/roles/global') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/roles/projects',
+              label: 'roles.page',
+              active: <%= request.request_uri.include?('/roles/projects') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/admin_dashboards/index',
+              label: 'default_dashboards.page',
+              active: <%= request.request_uri.include?('/admin_dashboards') ? 'true' : 'false' -%>
+            }
+          ]
+        },
+        {
+          label: 'sidebar.system',
+          active: false,
+          menu: [
+            <% if has_role?("provisioning") %>
+            {
+              url: '/provisioning/index',
+              label: 'provisioning.page',
+              active: <%= request.request_uri.include?('/provisioning') ? 'true' : 'false' -%>
+            },
+            <% end %>
+            {
+              url: '/bulk_deletion/index',
+              label: 'bulk_deletion.page',
+              active: <%= request.request_uri.include?('/bulk_deletion') ? 'true' : 'false' -%>
+            },
+            <% update_center_activated = controller.java_facade.getSettings().getBoolean('sonar.updatecenter.activate')
+                         if update_center_activated %>
+            {
+              url: '/updatecenter/index',
+              label: 'update_center.page',
+              active: <%= request.request_uri.include?('/updatecenter') ? 'true' : 'false' -%>
+            },
+            <% end %>
+            {
+              url: '/system/index',
+              label: 'system_info.page',
+              active: <%= request.request_uri.include?('/system') ? 'true' : 'false' -%>
+            },
+            {
+              url: '/analysis_reports/index',
+              label: 'analysis_reports.page',
+              active: <%= request.request_uri.include?('/analysis_reports') ? 'true' : 'false' -%>
+            }
+          ]
+        }
+      ];
+</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_recent_history.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_recent_history.html.erb
new file mode 100644 (file)
index 0000000..51b81a4
--- /dev/null
@@ -0,0 +1,9 @@
+<script>
+  var sonarRecentHistory = new Sonar.RecentHistory();
+  <% if @resource && Project.root_qualifiers.include?(@resource.qualifier) %>
+  sonarRecentHistory.add(
+      '<%= escape_javascript(h(@resource.key)) -%>',
+      '<%= escape_javascript(h(@resource.name)) -%>',
+      '<%= escape_javascript @resource.qualifier.downcase -%>');
+  <% end %>
+</script>
index 3c67adf30b2e4bb81e79369821c8cf723f34524a..52e8974dc14ebf90a1fc7ebb2810e2d990945fa0 100644 (file)
    end
   %>
 <%= render :partial => 'layouts/head' unless params[:hd]=='false' %>
+<%= render :partial => 'layouts/navbar' %>
 <% if params[:layout]=='false' %>
   <%= render :partial => 'layouts/nolayout' %>
 <% else %>
   <%= render :partial => 'layouts/layout' %>
 <% end %>
-<%= render :partial => 'layouts/footer' unless params[:hd]=='false' %>
\ No newline at end of file
+<%= render :partial => 'layouts/footer' unless params[:hd]=='false' %>
index d2c6ebcae2cb4a7bc5532d3ffa2e5016c104a0da..55ae2ad65ec4ec1f4c19f3b6c0103c9b6b9e3d91 100644 (file)
@@ -1 +1,3 @@
-<%= render 'settings', :project => nil %>
\ No newline at end of file
+<div class="page">
+  <%= render 'settings', :project => nil %>
+</div>