Sfoglia il codice sorgente

Merge branch 'master' of github.com:torkelo/grafana-private into pro

Conflicts:
	src/app/app.js
	src/app/controllers/all.js
	src/app/routes/all.js
	src/app/services/datasourceSrv.js
	src/test/test-main.js
Torkel Ödegaard 11 anni fa
parent
commit
75c77a44c9
98 ha cambiato i file con 225 aggiunte e 161 eliminazioni
  1. 5 2
      CHANGELOG.md
  2. 2 2
      latest.json
  3. 1 1
      package.json
  4. 1 1
      src/app/app.js
  5. 1 1
      src/app/components/kbn.js
  6. 1 12
      src/app/controllers/all.js
  7. 0 1
      src/app/controllers/pro/accountCtrl.js
  8. 0 1
      src/app/controllers/pro/datasourcesCtrl.js
  9. 7 0
      src/app/controllers/search.js
  10. 0 3
      src/app/directives/all.js
  11. 1 1
      src/app/directives/grafanaPanel.js
  12. 7 0
      src/app/features/all.js
  13. 2 1
      src/app/features/annotations/annotationsSrv.js
  14. 0 0
      src/app/features/annotations/editorCtrl.js
  15. 1 1
      src/app/features/annotations/partials/editor.html
  16. 14 0
      src/app/features/dashboard/all.js
  17. 0 0
      src/app/features/dashboard/dashboardCtrl.js
  18. 1 0
      src/app/features/dashboard/dashboardNavCtrl.js
  19. 0 1
      src/app/features/dashboard/dashboardSrv.js
  20. 0 0
      src/app/features/dashboard/keybindings.js
  21. 0 0
      src/app/features/dashboard/panelSrv.js
  22. 0 0
      src/app/features/dashboard/playlistCtrl.js
  23. 0 0
      src/app/features/dashboard/playlistSrv.js
  24. 0 0
      src/app/features/dashboard/rowCtrl.js
  25. 0 0
      src/app/features/dashboard/sharePanelCtrl.js
  26. 0 0
      src/app/features/dashboard/submenuCtrl.js
  27. 0 0
      src/app/features/dashboard/timeSrv.js
  28. 0 0
      src/app/features/dashboard/unsavedChangesSrv.js
  29. 0 0
      src/app/features/dashboard/viewStateSrv.js
  30. 1 1
      src/app/features/elasticsearch/datasource.js
  31. 0 0
      src/app/features/elasticsearch/partials/annotations.editor.html
  32. 1 1
      src/app/features/graphite/addGraphiteFunc.js
  33. 9 5
      src/app/features/graphite/datasource.js
  34. 0 0
      src/app/features/graphite/funcEditor.js
  35. 0 0
      src/app/features/graphite/gfunc.js
  36. 0 0
      src/app/features/graphite/lexer.js
  37. 0 0
      src/app/features/graphite/parser.js
  38. 0 0
      src/app/features/graphite/partials/annotations.editor.html
  39. 1 1
      src/app/features/graphite/partials/query.editor.html
  40. 3 3
      src/app/features/graphite/queryCtrl.js
  41. 6 4
      src/app/features/influxdb/datasource.js
  42. 0 0
      src/app/features/influxdb/funcEditor.js
  43. 14 12
      src/app/features/influxdb/influxSeries.js
  44. 0 0
      src/app/features/influxdb/partials/annotations.editor.html
  45. 1 1
      src/app/features/influxdb/partials/query.editor.html
  46. 0 0
      src/app/features/influxdb/queryBuilder.js
  47. 1 1
      src/app/features/influxdb/queryCtrl.js
  48. 4 3
      src/app/features/opentsdb/datasource.js
  49. 1 1
      src/app/features/opentsdb/partials/query.editor.html
  50. 1 1
      src/app/features/opentsdb/queryCtrl.js
  51. 0 0
      src/app/features/templating/editorCtrl.js
  52. 2 0
      src/app/features/templating/templateSrv.js
  53. 0 0
      src/app/features/templating/templateValuesSrv.js
  54. 13 4
      src/app/panels/graph/graph.tooltip.js
  55. 3 5
      src/app/panels/graph/module.js
  56. 3 3
      src/app/panels/singlestat/editor.html
  57. 2 1
      src/app/panels/singlestat/module.js
  58. 1 1
      src/app/partials/submenu.html
  59. 0 7
      src/app/routes/all.js
  60. 0 0
      src/app/routes/backend/admin.js
  61. 6 0
      src/app/routes/backend/all.js
  62. 0 0
      src/app/routes/backend/dashboard.js
  63. 0 0
      src/app/routes/backend/login.js
  64. 0 0
      src/app/routes/backend/solo-panel.js
  65. 0 20
      src/app/routes/dashboard-default.js
  66. 23 0
      src/app/routes/standalone/all.js
  67. 0 0
      src/app/routes/standalone/fromDB.js
  68. 0 0
      src/app/routes/standalone/fromFile.js
  69. 0 0
      src/app/routes/standalone/fromScript.js
  70. 2 10
      src/app/services/all.js
  71. 0 0
      src/app/services/backendSrv.js
  72. 3 5
      src/app/services/datasourceSrv.js
  73. 0 0
      src/app/services/grafanaDatasource.js
  74. 1 0
      src/css/less/panel.less
  75. 2 2
      src/plugins/datasource.example.js
  76. 3 3
      src/test/specs/dashboardSrv-specs.js
  77. 1 1
      src/test/specs/dashboardViewStateSrv-specs.js
  78. 1 1
      src/test/specs/gfunc-specs.js
  79. 2 1
      src/test/specs/graph-ctrl-specs.js
  80. 1 1
      src/test/specs/graph-specs.js
  81. 2 2
      src/test/specs/graphiteDatasource-specs.js
  82. 5 5
      src/test/specs/graphiteTargetCtrl-specs.js
  83. 2 2
      src/test/specs/influxQueryBuilder-specs.js
  84. 22 1
      src/test/specs/influxSeries-specs.js
  85. 2 2
      src/test/specs/influxdb-datasource-specs.js
  86. 7 0
      src/test/specs/kbn-format-specs.js
  87. 1 1
      src/test/specs/lexer-specs.js
  88. 1 1
      src/test/specs/parser-specs.js
  89. 2 2
      src/test/specs/row-ctrl-specs.js
  90. 1 1
      src/test/specs/seriesOverridesCtrl-specs.js
  91. 2 2
      src/test/specs/sharePanelCtrl-specs.js
  92. 1 1
      src/test/specs/templateSrv-specs.js
  93. 2 2
      src/test/specs/templateValuesSrv-specs.js
  94. 3 2
      src/test/specs/timeSrv-specs.js
  95. 13 9
      src/test/test-main.js
  96. 1 0
      tasks/build_task.js
  97. 1 0
      tasks/options/jscs.js
  98. 1 1
      tasks/options/requirejs.js

+ 5 - 2
CHANGELOG.md

@@ -1,9 +1,12 @@
-# 1.9.1 (unreleased)
+# 1.9.1 (2014-12-29)
 
 **Enhancements**
-- [Issue #1028](https://github.com/grafana/grafana/issues/1028). Graph: New legend option ``hideEmtpy`` to hide series with only null values
+- [Issue #1028](https://github.com/grafana/grafana/issues/1028). Graph: New legend option ``hideEmtpy`` to hide series with only null values from legend
+- [Issue #1242](https://github.com/grafana/grafana/issues/1242). OpenTSDB: Downsample query field now supports interval template variable
+- [Issue #1126](https://github.com/grafana/grafana/issues/1126). InfluxDB: Support more than 10 series name segments when using alias ``$number`` patterns
 
 **Fixes**
+- [Issue #1251](https://github.com/grafana/grafana/issues/1251). Graph: Fix for y axis and scaled units (GiB etc) caused rounding, for example 400 GiB instead of 378 GiB
 - [Issue #1199](https://github.com/grafana/grafana/issues/1199). Graph: fix for series tooltip when one series is hidden/disabled
 - [Issue #1207](https://github.com/grafana/grafana/issues/1207). Graphite: movingAverage / movingMedian parameter type impovement, now handles int and interval parameter
 

+ 2 - 2
latest.json

@@ -1,4 +1,4 @@
 {
-	"version": "1.9.0",
-	"url": "http://grafanarel.s3.amazonaws.com/grafana-1.9.0.tar.gz"
+	"version": "1.9.1",
+	"url": "http://grafanarel.s3.amazonaws.com/grafana-1.9.1.tar.gz"
 }

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
     "company": "Coding Instinct AB"
   },
   "name": "grafana",
-  "version": "1.9.0",
+  "version": "1.9.1",
   "repository": {
     "type": "git",
     "url": "http://github.com/torkelo/grafana.git"

+ 1 - 1
src/app/app.js

@@ -85,7 +85,7 @@ function (angular, $, _, appLevelRequire, config) {
     'directives/all',
     'filters/all',
     'components/partials',
-    'routes/all',
+    'routes/backend/all',
   ];
 
   _.each(config.plugins.dependencies, function(dep) {

+ 1 - 1
src/app/components/kbn.js

@@ -339,7 +339,7 @@ function($, _, moment) {
       return "";
     }
 
-    var factor = decimals ? Math.pow(10, decimals) : 1;
+    var factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1;
     var formatted = String(Math.round(value * factor) / factor);
 
     // if exponent return directly

+ 1 - 12
src/app/controllers/all.js

@@ -1,20 +1,9 @@
 define([
-  './pro/grafanaCtrl',
-  './pro/sharePanelCtrl',
-  './dashboardCtrl',
-  './dashboardNavCtrl',
-  './row',
-  './submenuCtrl',
+  './grafanaCtrl',
   './pulldown',
   './search',
   './metricKeys',
-  './graphiteTarget',
   './graphiteImport',
-  './influxTargetCtrl',
-  './playlistCtrl',
   './inspectCtrl',
-  './opentsdbTargetCtrl',
-  './annotationsEditorCtrl',
-  './templateEditorCtrl',
   './jsonEditorCtrl',
 ], function () {});

+ 0 - 1
src/app/controllers/pro/accountCtrl.js

@@ -1,6 +1,5 @@
 define([
   'angular',
-  'services/pro/backendSrv',
 ],
 function (angular) {
   'use strict';

+ 0 - 1
src/app/controllers/pro/datasourcesCtrl.js

@@ -1,6 +1,5 @@
 define([
   'angular',
-  'services/pro/backendSrv',
 ],
 function (angular) {
   'use strict';

+ 7 - 0
src/app/controllers/search.js

@@ -19,6 +19,9 @@ function (angular, _, config, $) {
       $scope.db = datasourceSrv.getGrafanaDB();
       $scope.currentSearchId = 0;
 
+      // events
+      $scope.onAppEvent('dashboard-deleted', $scope.dashboardDeleted);
+
       $timeout(function() {
         $scope.giveSearchFocus = $scope.giveSearchFocus + 1;
         $scope.query.query = 'title:';
@@ -123,6 +126,10 @@ function (angular, _, config, $) {
     $scope.deleteDashboard = function(dash, evt) {
       evt.stopPropagation();
       $scope.appEvent('delete-dashboard', { id: dash.id, title: dash.title });
+    };
+
+    $scope.dashboardDeleted = function(evt, id) {
+      var dash = _.findWhere($scope.results.dashboards, {id: id});
       $scope.results.dashboards = _.without($scope.results.dashboards, dash);
     };
 

+ 0 - 3
src/app/directives/all.js

@@ -12,11 +12,8 @@ define([
   './spectrumPicker',
   './bootstrap-tagsinput',
   './bodyClass',
-  './addGraphiteFunc',
-  './graphiteFuncEditor',
   './templateParamSelector',
   './graphiteSegment',
   './grafanaVersionCheck',
   './dropdown.typeahead',
-  './influxdbFuncEditor'
 ], function () {});

+ 1 - 1
src/app/directives/grafanaPanel.js

@@ -18,7 +18,7 @@ function (angular, $, config) {
       '<div class="panel-header">'+
           '<span class="alert-error panel-error small pointer"' +
                 'config-modal="app/partials/inspector.html" ng-if="panelMeta.error">' +
-            '<span data-placement="right" bs-tooltip="panelMeta.error">' +
+            '<span data-placement="top" bs-tooltip="panelMeta.error">' +
             '<i class="icon-exclamation-sign"></i><span class="panel-error-arrow"></span>' +
             '</span>' +
           '</span>' +

+ 7 - 0
src/app/features/all.js

@@ -1,3 +1,10 @@
 define([
   './panellinkeditor/module',
+  './annotations/annotationsSrv',
+  './templating/templateSrv',
+  './graphite/datasource',
+  './influxdb/datasource',
+  './opentsdb/datasource',
+  './elasticsearch/datasource',
+  './dashboard/all',
 ], function () {});

+ 2 - 1
src/app/services/annotationsSrv.js → src/app/features/annotations/annotationsSrv.js

@@ -1,7 +1,8 @@
 define([
   'angular',
   'lodash',
-  'moment'
+  'moment',
+  './editorCtrl'
 ], function (angular, _, moment) {
   'use strict';
 

+ 0 - 0
src/app/controllers/annotationsEditorCtrl.js → src/app/features/annotations/editorCtrl.js


+ 1 - 1
src/app/partials/annotations_editor.html → src/app/features/annotations/partials/editor.html

@@ -16,7 +16,7 @@
 	<div class="dashboard-editor-body">
 		<div class="editor-row row" ng-if="editor.index == 0">
 			<div class="span6">
-				<div ng-if="variables.length === 0">
+				<div ng-if="annotations.length === 0">
 					<em>No annotations defined</em>
 				</div>
 				<table class="grafana-options-table">

+ 14 - 0
src/app/features/dashboard/all.js

@@ -0,0 +1,14 @@
+define([
+  './dashboardCtrl',
+  './dashboardNavCtrl',
+  './playlistCtrl',
+  './rowCtrl',
+  './sharePanelCtrl',
+  './submenuCtrl',
+  './dashboardSrv',
+  './keybindings',
+  './viewStateSrv',
+  './playlistSrv',
+  './panelSrv',
+  './timeSrv',
+], function () {});

+ 0 - 0
src/app/controllers/dashboardCtrl.js → src/app/features/dashboard/dashboardCtrl.js


+ 1 - 0
src/app/controllers/dashboardNavCtrl.js → src/app/features/dashboard/dashboardNavCtrl.js

@@ -107,6 +107,7 @@ function (angular, _, moment, config, store) {
     $scope.deleteDashboardConfirmed = function(options) {
       var id = options.id;
       $scope.db.deleteDashboard(id).then(function(id) {
+        $scope.appEvent('dashboard-deleted', id);
         $scope.appEvent('alert-success', ['Dashboard Deleted', id + ' has been deleted']);
       }, function(err) {
         $scope.appEvent('alert-error', ['Deleted failed', err]);

+ 0 - 1
src/app/services/dashboard/dashboardSrv.js → src/app/features/dashboard/dashboardSrv.js

@@ -4,7 +4,6 @@ define([
   'kbn',
   'lodash',
   'moment',
-  '../timer',
 ],
 function (angular, $, kbn, _, moment) {
   'use strict';

+ 0 - 0
src/app/services/dashboard/dashboardKeyBindings.js → src/app/features/dashboard/keybindings.js


+ 0 - 0
src/app/services/panelSrv.js → src/app/features/dashboard/panelSrv.js


+ 0 - 0
src/app/controllers/playlistCtrl.js → src/app/features/dashboard/playlistCtrl.js


+ 0 - 0
src/app/services/playlistSrv.js → src/app/features/dashboard/playlistSrv.js


+ 0 - 0
src/app/controllers/row.js → src/app/features/dashboard/rowCtrl.js


+ 0 - 0
src/app/controllers/sharePanelCtrl.js → src/app/features/dashboard/sharePanelCtrl.js


+ 0 - 0
src/app/controllers/submenuCtrl.js → src/app/features/dashboard/submenuCtrl.js


+ 0 - 0
src/app/services/timeSrv.js → src/app/features/dashboard/timeSrv.js


+ 0 - 0
src/app/services/unsavedChangesSrv.js → src/app/features/dashboard/unsavedChangesSrv.js


+ 0 - 0
src/app/services/dashboard/dashboardViewStateSrv.js → src/app/features/dashboard/viewStateSrv.js


+ 1 - 1
src/app/services/elasticsearch/es-datasource.js → src/app/features/elasticsearch/datasource.js

@@ -24,7 +24,7 @@ function (angular, _, config, kbn, moment) {
       this.saveTemp = _.isUndefined(datasource.save_temp) ? true : datasource.save_temp;
       this.saveTempTTL = _.isUndefined(datasource.save_temp_ttl) ? '30d' : datasource.save_temp_ttl;
 
-      this.annotationEditorSrc = 'app/partials/elasticsearch/annotation_editor.html';
+      this.annotationEditorSrc = 'app/features/elasticsearch/partials/annotations.editor.html';
       this.supportAnnotations = true;
       this.supportMetrics = false;
     }

+ 0 - 0
src/app/partials/elasticsearch/annotation_editor.html → src/app/features/elasticsearch/partials/annotations.editor.html


+ 1 - 1
src/app/directives/addGraphiteFunc.js → src/app/features/graphite/addGraphiteFunc.js

@@ -3,7 +3,7 @@ define([
   'app',
   'lodash',
   'jquery',
-  '../services/graphite/gfunc',
+  './gfunc',
 ],
 function (angular, app, _, $, gfunc) {
   'use strict';

+ 9 - 5
src/app/services/graphite/graphiteDatasource.js → src/app/features/graphite/datasource.js

@@ -4,7 +4,10 @@ define([
   'jquery',
   'config',
   'kbn',
-  'moment'
+  'moment',
+  './queryCtrl',
+  './funcEditor',
+  './addGraphiteFunc',
 ],
 function (angular, _, $, config, kbn, moment) {
   'use strict';
@@ -17,14 +20,15 @@ function (angular, _, $, config, kbn, moment) {
       this.type = 'graphite';
       this.basicAuth = datasource.basicAuth;
       this.url = datasource.url;
-      this.editorSrc = 'app/partials/graphite/editor.html';
       this.name = datasource.name;
+      this.cacheTimeout = datasource.cacheTimeout;
+      this.withCredentials = datasource.withCredentials;
       this.render_method = datasource.render_method || 'POST';
+
       this.supportAnnotations = true;
       this.supportMetrics = true;
-      this.annotationEditorSrc = 'app/partials/graphite/annotation_editor.html';
-      this.cacheTimeout = datasource.cacheTimeout;
-      this.withCredentials = datasource.withCredentials;
+      this.editorSrc = 'app/features/graphite/partials/query.editor.html';
+      this.annotationEditorSrc = 'app/features/graphite/partials/annotations.editor.html';
     }
 
     GraphiteDatasource.prototype.query = function(options) {

+ 0 - 0
src/app/directives/graphiteFuncEditor.js → src/app/features/graphite/funcEditor.js


+ 0 - 0
src/app/services/graphite/gfunc.js → src/app/features/graphite/gfunc.js


+ 0 - 0
src/app/services/graphite/lexer.js → src/app/features/graphite/lexer.js


+ 0 - 0
src/app/services/graphite/parser.js → src/app/features/graphite/parser.js


+ 0 - 0
src/app/partials/graphite/annotation_editor.html → src/app/features/graphite/partials/annotations.editor.html


+ 1 - 1
src/app/partials/graphite/editor.html → src/app/features/graphite/partials/query.editor.html

@@ -3,7 +3,7 @@
 	<div  ng-repeat="target in panel.targets"
         class="grafana-target"
         ng-class="{'grafana-target-hidden': target.hide}"
-        ng-controller="GraphiteTargetCtrl"
+        ng-controller="GraphiteQueryCtrl"
         ng-init="init()">
 
     <div class="grafana-target-inner">

+ 3 - 3
src/app/controllers/graphiteTarget.js → src/app/features/graphite/queryCtrl.js

@@ -2,8 +2,8 @@ define([
   'angular',
   'lodash',
   'config',
-  '../services/graphite/gfunc',
-  '../services/graphite/parser'
+  './gfunc',
+  './parser'
 ],
 function (angular, _, config, gfunc, Parser) {
   'use strict';
@@ -11,7 +11,7 @@ function (angular, _, config, gfunc, Parser) {
   var module = angular.module('grafana.controllers');
   var targetLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'];
 
-  module.controller('GraphiteTargetCtrl', function($scope, $sce, templateSrv) {
+  module.controller('GraphiteQueryCtrl', function($scope, $sce, templateSrv) {
 
     $scope.init = function() {
       $scope.target.target = $scope.target.target || '';

+ 6 - 4
src/app/services/influxdb/influxdbDatasource.js → src/app/features/influxdb/datasource.js

@@ -3,7 +3,9 @@ define([
   'lodash',
   'kbn',
   './influxSeries',
-  './influxQueryBuilder'
+  './queryBuilder',
+  './queryCtrl',
+  './funcEditor',
 ],
 function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
   'use strict';
@@ -14,20 +16,20 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
 
     function InfluxDatasource(datasource) {
       this.type = 'influxDB';
-      this.editorSrc = 'app/partials/influxdb/editor.html';
       this.urls = datasource.urls;
       this.username = datasource.username;
       this.password = datasource.password;
       this.name = datasource.name;
       this.basicAuth = datasource.basicAuth;
+      this.grafanaDB = datasource.grafanaDB;
 
       this.saveTemp = _.isUndefined(datasource.save_temp) ? true : datasource.save_temp;
       this.saveTempTTL = _.isUndefined(datasource.save_temp_ttl) ? '30d' : datasource.save_temp_ttl;
 
-      this.grafanaDB = datasource.grafanaDB;
       this.supportAnnotations = true;
       this.supportMetrics = true;
-      this.annotationEditorSrc = 'app/partials/influxdb/annotation_editor.html';
+      this.editorSrc = 'app/features/influxdb/partials/query.editor.html';
+      this.annotationEditorSrc = 'app/features/influxdb/partials/annotations.editor.html';
     }
 
     InfluxDatasource.prototype.query = function(options) {

+ 0 - 0
src/app/directives/influxdbFuncEditor.js → src/app/features/influxdb/funcEditor.js


+ 14 - 12
src/app/services/influxdb/influxSeries.js → src/app/features/influxdb/influxSeries.js

@@ -106,21 +106,23 @@ function (_) {
   };
 
   p.createNameForSeries = function(seriesName, groupByColValue) {
-    var name = this.alias
-      .replace('$s', seriesName);
-
+    var regex = /\$(\w+)/g;
     var segments = seriesName.split('.');
-    for (var i = 0; i < segments.length; i++) {
-      if (segments[i].length > 0) {
-        name = name.replace('$' + i, segments[i]);
-      }
-    }
 
-    if (this.groupByField) {
-      name = name.replace('$g', groupByColValue);
-    }
+    return this.alias.replace(regex, function(match, group) {
+      if (group === 's') {
+        return seriesName;
+      }
+      else if (group === 'g') {
+        return groupByColValue;
+      }
+      var index = parseInt(group);
+      if (_.isNumber(index) && index < segments.length) {
+        return segments[index];
+      }
+      return match;
+    });
 
-    return name;
   };
 
   return InfluxSeries;

+ 0 - 0
src/app/partials/influxdb/annotation_editor.html → src/app/features/influxdb/partials/annotations.editor.html


+ 1 - 1
src/app/partials/influxdb/editor.html → src/app/features/influxdb/partials/query.editor.html

@@ -3,7 +3,7 @@
   <div  ng-repeat="target in panel.targets"
         class="grafana-target"
         ng-class="{'grafana-target-hidden': target.hide}"
-        ng-controller="InfluxTargetCtrl"
+        ng-controller="InfluxQueryCtrl"
         ng-init="init()">
 
     <div class="grafana-target-inner-wrapper">

+ 0 - 0
src/app/services/influxdb/influxQueryBuilder.js → src/app/features/influxdb/queryBuilder.js


+ 1 - 1
src/app/controllers/influxTargetCtrl.js → src/app/features/influxdb/queryCtrl.js

@@ -9,7 +9,7 @@ function (angular, _) {
 
   var seriesList = null;
 
-  module.controller('InfluxTargetCtrl', function($scope, $timeout) {
+  module.controller('InfluxQueryCtrl', function($scope, $timeout) {
 
     $scope.init = function() {
       var target = $scope.target;

+ 4 - 3
src/app/services/opentsdb/opentsdbDatasource.js → src/app/features/opentsdb/datasource.js

@@ -2,7 +2,8 @@ define([
   'angular',
   'lodash',
   'kbn',
-  'moment'
+  'moment',
+  './queryCtrl',
 ],
 function (angular, _, kbn) {
   'use strict';
@@ -13,7 +14,7 @@ function (angular, _, kbn) {
 
     function OpenTSDBDatasource(datasource) {
       this.type = 'opentsdb';
-      this.editorSrc = 'app/partials/opentsdb/editor.html';
+      this.editorSrc = 'app/features/opentsdb/partials/query.editor.html';
       this.url = datasource.url;
       this.name = datasource.name;
       this.supportMetrics = true;
@@ -148,7 +149,7 @@ function (angular, _, kbn) {
       }
 
       if (target.shouldDownsample) {
-        query.downsample = target.downsampleInterval + "-" + target.downsampleAggregator;
+        query.downsample = templateSrv.replace(target.downsampleInterval) + "-" + target.downsampleAggregator;
       }
 
       query.tags = angular.copy(target.tags);

+ 1 - 1
src/app/partials/opentsdb/editor.html → src/app/features/opentsdb/partials/query.editor.html

@@ -2,7 +2,7 @@
   <div  ng-repeat="target in panel.targets"
         class="grafana-target"
         ng-class="{'grafana-target-hidden': target.hide}"
-        ng-controller="OpenTSDBTargetCtrl"
+        ng-controller="OpenTSDBQueryCtrl"
         ng-init="init()">
 
     <div class="grafana-target-inner-wrapper">

+ 1 - 1
src/app/controllers/opentsdbTargetCtrl.js → src/app/features/opentsdb/queryCtrl.js

@@ -8,7 +8,7 @@ function (angular, _, kbn) {
 
   var module = angular.module('grafana.controllers');
 
-  module.controller('OpenTSDBTargetCtrl', function($scope, $timeout) {
+  module.controller('OpenTSDBQueryCtrl', function($scope, $timeout) {
 
     $scope.init = function() {
       $scope.target.errors = validateTarget($scope.target);

+ 0 - 0
src/app/controllers/templateEditorCtrl.js → src/app/features/templating/editorCtrl.js


+ 2 - 0
src/app/services/templateSrv.js → src/app/features/templating/templateSrv.js

@@ -1,6 +1,8 @@
 define([
   'angular',
   'lodash',
+  './editorCtrl',
+  './templateValuesSrv',
 ],
 function (angular, _) {
   'use strict';

+ 0 - 0
src/app/services/templateValuesSrv.js → src/app/features/templating/templateValuesSrv.js


+ 13 - 4
src/app/panels/graph/graph.tooltip.js

@@ -70,7 +70,11 @@ function ($) {
 
       for (i = 0; i < seriesList.length; i++) {
         series = seriesList[i];
-        if (!series.data.length) { continue; }
+
+        if (!series.data.length) {
+          results.push({ hidden: true });
+          continue;
+        }
 
         if (scope.panel.stack) {
           if (scope.panel.tooltip.value_type === 'individual') {
@@ -99,9 +103,9 @@ function ($) {
             lasthoverIndex = hoverIndex;
           }
 
-          results.push({ value: value, hoverIndex: newhoverIndex, series: series });
+          results.push({ value: value, hoverIndex: newhoverIndex});
         } else {
-          results.push({ value: value, hoverIndex: hoverIndex, series: series });
+          results.push({ value: value, hoverIndex: hoverIndex});
         }
       }
 
@@ -150,7 +154,12 @@ function ($) {
 
         for (i = 0; i < seriesHoverInfo.length; i++) {
           hoverInfo = seriesHoverInfo[i];
-          series = hoverInfo.series;
+
+          if (hoverInfo.hidden) {
+            continue;
+          }
+
+          series = seriesList[i];
           value = series.formatValue(hoverInfo.value);
 
           seriesHtml += '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';

+ 3 - 5
src/app/panels/graph/module.js

@@ -7,9 +7,6 @@ define([
   'moment',
   'components/timeSeries',
   'components/panelmeta',
-  'services/panelSrv',
-  'services/annotationsSrv',
-  'services/datasourceSrv',
   './seriesOverridesCtrl',
   './graph',
   './legend',
@@ -146,10 +143,9 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
     };
 
     $scope.dataHandler = function(results) {
-      $scope.panelMeta.loading = false;
-
       // png renderer returns just a url
       if (_.isString(results)) {
+        $scope.panelMeta.loading = false;
         $scope.render(results);
         return;
       }
@@ -164,9 +160,11 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
 
       $scope.annotationsPromise
         .then(function(annotations) {
+          $scope.panelMeta.loading = false;
           $scope.seriesList.annotations = annotations;
           $scope.render($scope.seriesList);
         }, function() {
+          $scope.panelMeta.loading = false;
           $scope.render($scope.seriesList);
         });
     };

+ 3 - 3
src/app/panels/singlestat/editor.html

@@ -23,15 +23,15 @@
     <h5>Big value font size</h5>
 		<div class="editor-option">
 			<label class="small">Prefix</label>
-			<select class="input-mini" style="width: 75px;" ng-model="panel.prefixFontSize" ng-options="f for f in ['30%','50%','70%','80%','100%']" ng-change="render()"></select>
+			<select class="input-mini" style="width: 75px;" ng-model="panel.prefixFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
 		</div>
 		<div class="editor-option">
 			<label class="small">Value</label>
-			<select class="input-mini" style="width: 75px;" ng-model="panel.valueFontSize" ng-options="f for f in ['30%','50%','70%','80%','100%', '110%', '120%']" ng-change="render()"></select>
+			<select class="input-mini" style="width: 75px;" ng-model="panel.valueFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
 		</div>
 		<div class="editor-option">
 			<label class="small">Postfix</label>
-			<select class="input-mini" style="width: 75px;" ng-model="panel.postfixFontSize" ng-options="f for f in ['30%','50%','70%','80%','100%']" ng-change="render()"></select>
+			<select class="input-mini" style="width: 75px;" ng-model="panel.postfixFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
 		</div>
 	</div>
 

+ 2 - 1
src/app/panels/singlestat/module.js

@@ -5,7 +5,6 @@ define([
   'components/timeSeries',
   'kbn',
   'components/panelmeta',
-  'services/panelSrv',
   './singleStatPanel',
 ],
 function (angular, app, _, TimeSeries, kbn, PanelMeta) {
@@ -22,6 +21,8 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
       metricsEditor: true
     });
 
+    $scope.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
+
     $scope.panelMeta.addEditorTab('Options', 'app/panels/singlestat/editor.html');
 
     // Set and populate defaults

+ 1 - 1
src/app/partials/submenu.html

@@ -10,7 +10,7 @@
 							</a>
 							<ul class="dropdown-menu">
 								<li><a class="pointer" dash-editor-link="app/partials/templating_editor.html">Templating</a></li>
-								<li><a class="pointer" dash-editor-link="app/partials/annotations_editor.html">Annotations</a></li>
+								<li><a class="pointer" dash-editor-link="app/features/annotations/partials/editor.html">Annotations</a></li>
 							</ul>
 						</div>
 					</li>

+ 0 - 7
src/app/routes/all.js

@@ -1,7 +0,0 @@
-define([
-  './pro/dashboard',
-  './pro/admin',
-  './pro/solo-panel',
-  './pro/login',
-],
-function () {});

+ 0 - 0
src/app/routes/pro/admin.js → src/app/routes/backend/admin.js


+ 6 - 0
src/app/routes/backend/all.js

@@ -0,0 +1,6 @@
+define([
+  './dashboard',
+  './admin',
+  './login',
+  './solo-panel',
+], function() {});

+ 0 - 0
src/app/routes/pro/dashboard.js → src/app/routes/backend/dashboard.js


+ 0 - 0
src/app/routes/pro/login.js → src/app/routes/backend/login.js


+ 0 - 0
src/app/routes/pro/solo-panel.js → src/app/routes/backend/solo-panel.js


+ 0 - 20
src/app/routes/dashboard-default.js

@@ -1,20 +0,0 @@
-define([
-  'angular',
-  'config',
-  'store'
-],
-function (angular, config, store) {
-  "use strict";
-
-  var module = angular.module('grafana.routes');
-
-  module.config(function($routeProvider) {
-    $routeProvider
-      .when('/', {
-        redirectTo: function() {
-          return store.get('grafanaDashboardDefault') || config.default_route;
-        }
-      });
-  });
-
-});

+ 23 - 0
src/app/routes/standalone/all.js

@@ -0,0 +1,23 @@
+define([
+  'angular',
+  'config',
+  'store',
+  './fromDB',
+  './fromFile',
+  './fromScript',
+],
+function (angular, config, store) {
+  'use strict';
+
+  var module = angular.module('grafana.routes');
+
+  module.config(function($routeProvider) {
+    $routeProvider
+    .when('/', {
+      redirectTo: function() {
+        return store.get('grafanaDashboardDefault') || config.default_route;
+      }
+    });
+  });
+
+});

+ 0 - 0
src/app/routes/dashboard-from-db.js → src/app/routes/standalone/fromDB.js


+ 0 - 0
src/app/routes/dashboard-from-file.js → src/app/routes/standalone/fromFile.js


+ 0 - 0
src/app/routes/dashboard-from-script.js → src/app/routes/standalone/fromScript.js


+ 2 - 10
src/app/services/all.js

@@ -2,18 +2,10 @@ define([
   './alertSrv',
   './utilSrv',
   './datasourceSrv',
-  './timeSrv',
-  './templateSrv',
-  './templateValuesSrv',
-  './panelSrv',
   './timer',
   './keyboardManager',
-  './annotationsSrv',
   './popoverSrv',
-  './playlistSrv',
-  './unsavedChangesSrv',
-  './dashboard/dashboardKeyBindings',
-  './dashboard/dashboardSrv',
-  './dashboard/dashboardViewStateSrv',
+  './backendSrv',
+  './grafanaDatasource',
 ],
 function () {});

+ 0 - 0
src/app/services/pro/backendSrv.js → src/app/services/backendSrv.js


+ 3 - 5
src/app/services/datasourceSrv.js

@@ -2,11 +2,6 @@ define([
   'angular',
   'lodash',
   'config',
-  './graphite/graphiteDatasource',
-  './grafana/grafanaDatasource',
-  './influxdb/influxdbDatasource',
-  './opentsdb/opentsdbDatasource',
-  './elasticsearch/es-datasource',
 ],
 function (angular, _, config) {
   'use strict';
@@ -68,6 +63,9 @@ function (angular, _, config) {
       case 'opentsdb':
         Datasource = $injector.get('OpenTSDBDatasource');
         break;
+      case 'elasticsearch':
+        Datasource = $injector.get('ElasticDatasource');
+        break;
       case 'grafana':
         Datasource = $injector.get('GrafanaDatasource');
         break;

+ 0 - 0
src/app/services/grafana/grafanaDatasource.js → src/app/services/grafanaDatasource.js


+ 1 - 0
src/css/less/panel.less

@@ -62,6 +62,7 @@
   left: 0;
   padding: 0px 17px 6px 5px;
   top: 0;
+  z-index: 10;
   i {
     position: relative;
     top: -2px;

+ 2 - 2
src/plugins/datasource.example.js

@@ -21,8 +21,8 @@ function (angular, _, kbn) {
 
     CustomDatasource.prototype.query = function(options) {
       // get from & to in seconds
-      var from = kbn.parseDate(options.range.from).getTime() / 1000;
-      var to = kbn.parseDate(options.range.to).getTime() / 1000;
+      var from = kbn.parseDate(options.range.from).getTime();
+      var to = kbn.parseDate(options.range.to).getTime();
 
       var series = [];
       var stepInSeconds = (to - from) / options.maxDataPoints;

+ 3 - 3
src/test/specs/dashboardSrv-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/dashboard/dashboardSrv'
+  'features/dashboard/dashboardSrv'
 ], function() {
   'use strict';
 
@@ -95,7 +95,7 @@ define([
     beforeEach(module('grafana.services'));
     beforeEach(inject(function(dashboardSrv) {
       model = dashboardSrv.create({
-        services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [1] }},
+        services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [{}] }},
         pulldowns: [
           {
             type: 'filtering',
@@ -135,7 +135,7 @@ define([
 
     it('should move time and filtering list', function() {
       expect(model.time.from).to.be('now-1d');
-      expect(model.templating.list[0]).to.be(1);
+      expect(model.templating.list[0].allFormat).to.be('glob');
     });
 
     it('graphite panel should change name too graph', function() {

+ 1 - 1
src/test/specs/dashboardViewStateSrv-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/dashboard/dashboardViewStateSrv'
+  'features/dashboard/viewStateSrv'
 ], function() {
   'use strict';
 

+ 1 - 1
src/test/specs/gfunc-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/graphite/gfunc'
+  'features/graphite/gfunc'
 ], function(gfunc) {
   'use strict';
 

+ 2 - 1
src/test/specs/graph-ctrl-specs.js

@@ -1,5 +1,6 @@
 define([
-  './helpers',
+  'helpers',
+  'features/dashboard/panelSrv',
   'panels/graph/module'
 ], function(helpers) {
   'use strict';

+ 1 - 1
src/test/specs/graph-specs.js

@@ -1,5 +1,5 @@
 define([
-  './helpers',
+  'helpers',
   'angular',
   'jquery',
   'components/timeSeries',

+ 2 - 2
src/test/specs/graphiteDatasource-specs.js

@@ -1,6 +1,6 @@
 define([
-  './helpers',
-  'services/graphite/graphiteDatasource'
+  'helpers',
+  'features/graphite/datasource'
 ], function(helpers) {
   'use strict';
 

+ 5 - 5
src/test/specs/graphiteTargetCtrl-specs.js

@@ -1,16 +1,16 @@
 define([
-  './helpers',
-  'services/graphite/gfunc',
-  'controllers/graphiteTarget'
+  'helpers',
+  'features/graphite/gfunc',
+  'features/graphite/queryCtrl'
 ], function(helpers, gfunc) {
   'use strict';
 
-  describe('GraphiteTargetCtrl', function() {
+  describe('GraphiteQueryCtrl', function() {
     var ctx = new helpers.ControllerTestContext();
 
     beforeEach(module('grafana.controllers'));
     beforeEach(ctx.providePhase());
-    beforeEach(ctx.createControllerPhase('GraphiteTargetCtrl'));
+    beforeEach(ctx.createControllerPhase('GraphiteQueryCtrl'));
 
     beforeEach(function() {
       ctx.scope.target = {

+ 2 - 2
src/test/specs/influxQueryBuilder-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/influxdb/influxQueryBuilder'
+  'features/influxdb/queryBuilder'
 ], function(InfluxQueryBuilder) {
   'use strict';
 
@@ -68,7 +68,7 @@ define([
         var query = builder.build();
 
         expect(query).to.be('select mean(value) from "merge.google.test" where $timeFilter ' +
-          'group by time($interval) order asc'); 
+          'group by time($interval) order asc');
       });
 
     });

+ 22 - 1
src/test/specs/influxSeries-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/influxdb/influxSeries'
+  'features/influxdb/influxSeries'
 ], function(InfluxSeries) {
   'use strict';
 
@@ -80,6 +80,27 @@ define([
 
     });
 
+    describe('given an alias format and many segments', function() {
+      var series = new InfluxSeries({
+        seriesList: [
+          {
+            columns: ['time', 'mean', 'sequence_number'],
+            name: 'a0.a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11.a12',
+            points: [[1402596000, 10, 1], [1402596001, 12, 2]]
+          }
+        ],
+        alias: '$5.$11.mean'
+      });
+
+      var result = series.getTimeSeries();
+
+      it('should generate correct series name', function() {
+        expect(result[0].target).to.be('a5.a11.mean');
+      });
+
+    });
+
+
     describe('given an alias format with group by field', function() {
       var series = new InfluxSeries({
         seriesList: [

+ 2 - 2
src/test/specs/influxdb-datasource-specs.js

@@ -1,6 +1,6 @@
 define([
-  './helpers',
-  'services/influxdb/influxdbDatasource'
+  'helpers',
+  'features/influxdb/datasource'
 ], function(helpers) {
   'use strict';
 

+ 7 - 0
src/test/specs/kbn-format-specs.js

@@ -29,6 +29,13 @@ define([
   describeValueFormat('ns', 25, 1, 0, '25 ns');
   describeValueFormat('ns', 2558, 50, 0, '2.56 µs');
 
+  describe('kbn.toFixed and negative decimals', function() {
+    it('should treat as zero decimals', function() {
+      var str = kbn.toFixed(186.123, -2);
+      expect(str).to.be('186');
+    });
+  });
+
   describe('calculateInterval', function() {
     it('1h 100 resultion', function() {
       var range = { from: kbn.parseDate('now-1h'), to: kbn.parseDate('now') };

+ 1 - 1
src/test/specs/lexer-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/graphite/lexer'
+  'features/graphite/lexer'
 ], function(Lexer) {
   'use strict';
 

+ 1 - 1
src/test/specs/parser-specs.js

@@ -1,5 +1,5 @@
 define([
-  'services/graphite/parser'
+  'features/graphite/parser'
 ], function(Parser) {
   'use strict';
 

+ 2 - 2
src/test/specs/row-ctrl-specs.js

@@ -1,6 +1,6 @@
 define([
-  './helpers',
-  'controllers/row'
+  'helpers',
+  'features/dashboard/rowCtrl'
 ], function(helpers) {
   'use strict';
 

+ 1 - 1
src/test/specs/seriesOverridesCtrl-specs.js

@@ -1,5 +1,5 @@
 define([
-  './helpers',
+  'helpers',
   'panels/graph/seriesOverridesCtrl'
 ], function(helpers) {
   'use strict';

+ 2 - 2
src/test/specs/sharePanelCtrl-specs.js

@@ -1,6 +1,6 @@
 define([
-  './helpers',
-  'controllers/sharePanelCtrl'
+  'helpers',
+  'features/dashboard/sharePanelCtrl'
 ], function(helpers) {
   'use strict';
 

+ 1 - 1
src/test/specs/templateSrv-specs.js

@@ -1,7 +1,7 @@
 define([
   'mocks/dashboard-mock',
   'lodash',
-  'services/templateSrv'
+  'features/templating/templateSrv'
 ], function(dashboardMock) {
   'use strict';
 

+ 2 - 2
src/test/specs/templateValuesSrv-specs.js

@@ -1,8 +1,8 @@
 define([
   'mocks/dashboard-mock',
-  './helpers',
+  'helpers',
   'moment',
-  'services/templateValuesSrv'
+  'features/templating/templateValuesSrv'
 ], function(dashboardMock, helpers, moment) {
   'use strict';
 

+ 3 - 2
src/test/specs/timeSrv-specs.js

@@ -1,8 +1,9 @@
 define([
   'mocks/dashboard-mock',
-  './helpers',
+  'helpers',
   'lodash',
-  'services/timeSrv'
+  'services/timer',
+  'features/dashboard/timeSrv'
 ], function(dashboardMock, helpers, _) {
   'use strict';
 

+ 13 - 9
src/test/test-main.js

@@ -4,7 +4,8 @@ require.config({
   paths: {
     specs:                 '../test/specs',
     mocks:                 '../test/mocks',
-    config:                '../config.sample',
+    helpers:               '../test/specs/helpers',
+    config:                ['../config', '../config.sample'],   
     kbn:                   'components/kbn',
     store:                 'components/store',
 
@@ -96,9 +97,10 @@ require.config({
 
 require([
   'angular',
+  'config',
   'angularMocks',
   'app',
-], function(angular) {
+], function(angular, config) {
   'use strict';
 
   for (var file in window.__karma__.files) {
@@ -111,11 +113,9 @@ require([
   angular.module('grafana', ['ngRoute']);
   angular.module('grafana.services', ['ngRoute', '$strap.directives']);
   angular.module('grafana.panels', []);
-  angular.module('grafana.routes', ['ngRoute']);
   angular.module('grafana.filters', []);
 
-  require([
-    'specs/overview-ctrl-specs',
+ var specs = [
     'specs/lexer-specs',
     'specs/parser-specs',
     'specs/gfunc-specs',
@@ -136,12 +136,16 @@ require([
     'specs/templateValuesSrv-specs',
     'specs/kbn-format-specs',
     'specs/dashboardSrv-specs',
-    'specs/dashboardViewStateSrv-specs',
-    'specs/overview-ctrl-specs',
+    'specs/dashboardViewStateSrv-specs'
     'specs/pro/soloPanelCtrl-specs',
-  ], function () {
-    window.__karma__.start();
+  ];
+
+  var pluginSpecs = (config.plugins.specs || []).map(function (spec) {
+    return '../plugins/' + spec;
   });
 
+  require(specs.concat(pluginSpecs), function () {
+    window.__karma__.start();
+  });
 });
 

+ 1 - 0
tasks/build_task.js

@@ -4,6 +4,7 @@ module.exports = function(grunt) {
   grunt.registerTask('build', [
     'jshint:source',
     'jshint:tests',
+    'karma:test',
     'clean:on_start',
     'less:src',
     'concat:cssDark',

+ 1 - 0
tasks/options/jscs.js

@@ -3,6 +3,7 @@ module.exports = function(config) {
     src: [
       'Gruntfile.js',
       '<%= srcDir %>/app/**/*.js',
+      '<%= srcDir %>/plugins/**/*.js',
       '!<%= srcDir %>/app/panels/*/{lib,leaflet}/*',
       '!<%= srcDir %>/app/dashboards/*'
     ],

+ 1 - 1
tasks/options/requirejs.js

@@ -63,7 +63,7 @@ module.exports = function(config,grunt) {
         'directives/all',
         'filters/all',
         'controllers/all',
-        'routes/all',
+        'routes/standalone/default',
         'components/partials',
       ]
     }