Browse Source

Merge branch 'hkraemer-filtersrv-object-refactoring' of github.com:Tetha/grafana into Tetha-hkraemer-filtersrv-object-refactoring

Torkel Ödegaard 11 years ago
parent
commit
5ea07d0f07

+ 6 - 1
.gitignore

@@ -1,6 +1,11 @@
 node_modules
 .aws-config.json
 dist
+
+# locally required config files
 web.config
 config.js
-*.sublime-workspace
+
+# Editor junk
+*.sublime-workspace
+*.swp

+ 1 - 1
src/app/app.js

@@ -98,7 +98,7 @@ function (angular, $, _, appLevelRequire) {
     'pasvaz.bindonce'
   ];
 
-  _.each('controllers directives factories services filters'.split(' '),
+  _.each('controllers directives factories services services.dashboard filters'.split(' '),
   function (type) {
     var module_name = 'kibana.'+type;
     // create the module

+ 15 - 65
src/app/controllers/dash.js

@@ -22,7 +22,8 @@ define([
   'jquery',
   'config',
   'underscore',
-  'services/all'
+  'services/all',
+  'services/dashboard/all'
 ],
 function (angular, $, config, _) {
   "use strict";
@@ -30,7 +31,7 @@ function (angular, $, config, _) {
   var module = angular.module('kibana.controllers');
 
   module.controller('DashCtrl', function(
-    $scope, $rootScope, ejsResource, dashboard,
+    $scope, $rootScope, $timeout, ejsResource, dashboard, filterSrv, dashboardKeybindings,
     alertSrv, panelMove, keyboardManager, grafanaVersion) {
 
     $scope.requiredElasticSearchVersion = ">=0.90.3";
@@ -56,6 +57,16 @@ function (angular, $, config, _) {
       $scope.dashboard = dashboard;
       $scope.dashAlerts = alertSrv;
 
+      $scope.filter = filterSrv;
+      $scope.filter.init( dashboard.current );
+
+      $scope.$watch('dashboard.current', function(newValue) {
+          $scope.filter.init( newValue );
+      });
+
+      $scope.$watch('filter.time', function() {
+          $scope.dashboard.refresh();
+      }, true);
       // Clear existing alerts
       alertSrv.clearAll();
 
@@ -66,68 +77,7 @@ function (angular, $, config, _) {
       $scope.bindKeyboardShortcuts();
     };
 
-    $scope.bindKeyboardShortcuts = function() {
-      $rootScope.$on('panel-fullscreen-enter', function() {
-        $rootScope.fullscreen = true;
-      });
-
-      $rootScope.$on('panel-fullscreen-exit', function() {
-        $rootScope.fullscreen = false;
-      });
-
-      $rootScope.$on('dashboard-saved', function() {
-        if ($rootScope.fullscreen) {
-          $rootScope.$emit('panel-fullscreen-exit');
-        }
-      });
-
-      keyboardManager.bind('ctrl+f', function(evt) {
-        $rootScope.$emit('open-search', evt);
-      }, { inputDisabled: true });
-
-      keyboardManager.bind('ctrl+h', function() {
-        var current = dashboard.current.hideControls;
-        dashboard.current.hideControls = !current;
-        dashboard.current.panel_hints = current;
-      }, { inputDisabled: true });
-
-      keyboardManager.bind('ctrl+s', function(evt) {
-        $rootScope.$emit('save-dashboard', evt);
-      }, { inputDisabled: true });
-
-      keyboardManager.bind('ctrl+r', function() {
-        dashboard.refresh();
-      }, { inputDisabled: true });
-
-      keyboardManager.bind('ctrl+z', function(evt) {
-        $rootScope.$emit('zoom-out', evt);
-      }, { inputDisabled: true });
-
-      keyboardManager.bind('esc', function() {
-        var popups = $('.popover.in');
-        if (popups.length > 0) {
-          return;
-        }
-        $rootScope.$emit('panel-fullscreen-exit');
-      }, { inputDisabled: true });
-    };
-
-    $scope.countWatchers = function (scopeStart) {
-      var q = [scopeStart || $rootScope], watchers = 0, scope;
-      while (q.length > 0) {
-        scope = q.pop();
-        if (scope.$$watchers) {
-          watchers += scope.$$watchers.length;
-        }
-        if (scope.$$childHead) {
-          q.push(scope.$$childHead);
-        }
-        if (scope.$$nextSibling) {
-          q.push(scope.$$nextSibling);
-        }
-      }
-      window.console.log(watchers);
-    };
+    $scope.bindKeyboardShortcuts = dashboardKeybindings.shortcuts;
 
     $scope.isPanel = function(obj) {
       if(!_.isNull(obj) && !_.isUndefined(obj) && !_.isUndefined(obj.type)) {
@@ -196,4 +146,4 @@ function (angular, $, config, _) {
 
     $scope.init();
   });
-});
+});

+ 3 - 3
src/app/controllers/dashLoader.js

@@ -8,7 +8,7 @@ function (angular, _, moment) {
 
   var module = angular.module('kibana.controllers');
 
-  module.controller('dashLoader', function($scope, $rootScope, $http, dashboard, alertSrv, $location, filterSrv, playlistSrv) {
+  module.controller('dashLoader', function($scope, $rootScope, $http, dashboard, alertSrv, $location, playlistSrv) {
     $scope.loader = dashboard.current.loader;
 
     $scope.init = function() {
@@ -131,7 +131,7 @@ function (angular, _, moment) {
     // function $scope.zoom
     // factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
     $scope.zoom = function(factor) {
-      var _range = filterSrv.timeRange();
+      var _range = this.filter.timeRange();
       var _timespan = (_range.to.valueOf() - _range.from.valueOf());
       var _center = _range.to.valueOf() - _timespan/2;
 
@@ -145,7 +145,7 @@ function (angular, _, moment) {
         _to = Date.now();
       }
 
-      filterSrv.setTime({
+      this.filter.setTime({
         from:moment.utc(_from).toDate(),
         to:moment.utc(_to).toDate(),
       });

+ 6 - 6
src/app/controllers/graphiteTarget.js

@@ -10,7 +10,7 @@ function (angular, _, config, gfunc, Parser) {
 
   var module = angular.module('kibana.controllers');
 
-  module.controller('GraphiteTargetCtrl', function($scope, $http, filterSrv) {
+  module.controller('GraphiteTargetCtrl', function($scope) {
 
     $scope.init = function() {
       parseTarget();
@@ -120,7 +120,7 @@ function (angular, _, config, gfunc, Parser) {
       }
 
       var path = getSegmentPathUpTo(fromIndex + 1);
-      return $scope.datasource.metricFindQuery(path)
+      return $scope.datasource.metricFindQuery($scope.filter, path)
         .then(function(segments) {
           if (segments.length === 0) {
             $scope.segments = $scope.segments.splice(0, fromIndex);
@@ -157,17 +157,17 @@ function (angular, _, config, gfunc, Parser) {
       var query = index === 0 ?
         '*' : getSegmentPathUpTo(index) + '.*';
 
-      return $scope.datasource.metricFindQuery(query)
+      return $scope.datasource.metricFindQuery($scope.filter, query)
         .then(function(segments) {
           _.each(segments, function(segment) {
             segment.html = segment.val = segment.text;
           });
 
-          _.each(filterSrv.list, function(filter) {
+          _.each($scope.filter.templateParameters, function( templateParameter ) {
             segments.unshift({
               type: 'template',
-              html: '[[' + filter.name + ']]',
-              val: '[[' + filter.name + ']]',
+              html: '[[' + templateParameter.name + ']]',
+              val: '[[' + templateParameter.name + ']]',
               expandable: true,
             });
           });

+ 15 - 7
src/app/controllers/metricKeys.js

@@ -52,16 +52,23 @@ function (angular, _, config) {
     $scope.loadAll = function() {
       $scope.infoText = "Fetching all metrics from graphite...";
 
-      return $http.get(config.graphiteUrl + "/metrics/index.json")
-        .then(saveMetricsArray)
-        .then(function () {
+      getFromEachGraphite( '/metrics/index.json', saveMetricsArray )
+        .then( function() {
           $scope.infoText = "Indexing complete!";
-        })
-        .then(null, function(err) {
+        }).then(null, function(err) {
           $scope.errorText = err;
         });
     };
 
+    function getFromEachGraphite( request, data_callback, error_callback ) {
+      return $q.all( _.map( config.datasources, function( datasource ) {
+        if ( datasource.type = 'graphite' ) {
+          return $http.get( datasource.url + request )
+            .then( data_callback, error_callback );
+        } 
+      } ) );
+    }
+
     function saveMetricsArray(data, currentIndex)
     {
       if (!data && !data.data && data.data.length === 0) {
@@ -80,6 +87,7 @@ function (angular, _, config) {
         });
     }
 
+    
     function deleteIndex()
     {
       var deferred = $q.defer();
@@ -172,9 +180,9 @@ function (angular, _, config) {
 
     function loadMetricsRecursive(metricPath)
     {
-      return $http.get(config.graphiteUrl + '/metrics/find/?query=' + metricPath).then(receiveMetric);
+      return getFromEachGraphite( '/metrics/find/?query=' + metricPath, receiveMetric );
     }
 
   });
 
-});
+});

+ 6 - 4
src/app/directives/grafanaGraph.js

@@ -10,7 +10,7 @@ function (angular, $, kbn, moment, _) {
 
   var module = angular.module('kibana.directives');
 
-  module.directive('grafanaGraph', function(filterSrv, $rootScope, dashboard) {
+  module.directive('grafanaGraph', function($rootScope, dashboard) {
     return {
       restrict: 'A',
       template: '<div> </div>',
@@ -387,9 +387,11 @@ function (angular, $, kbn, moment, _) {
         }
 
         elem.bind("plotselected", function (event, ranges) {
-          filterSrv.setTime({
-            from  : moment.utc(ranges.xaxis.from).toDate(),
-            to    : moment.utc(ranges.xaxis.to).toDate(),
+          scope.$apply( function() {
+              scope.filter.setTime({
+                from  : moment.utc(ranges.xaxis.from).toDate(),
+                to    : moment.utc(ranges.xaxis.to).toDate(),
+              });
           });
         });
       }

+ 2 - 2
src/app/panels/filtering/module.html

@@ -2,7 +2,7 @@
 
   <div class='filtering-container'>
 
-    <div ng-repeat="filter in filterSrv.list" class="small filter-panel-filter">
+    <div ng-repeat="filter in filter.list" class="small filter-panel-filter">
       <div>
         <i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(filter)"></i>
         <i class="filter-action pointer icon-edit" ng-hide="filter.editing" bs-tooltip="'Edit'" ng-click="filter.editing = true"></i>
@@ -47,4 +47,4 @@
     </div>
     <i class="pointer icon-plus-sign add-filter-action" ng-click="add()" bs-tooltip="'Add metric filter / param'" data-placement="right"></i>
   </div>
-</div>
+</div>

+ 25 - 17
src/app/panels/filtering/module.js

@@ -14,7 +14,7 @@ function (angular, app, _) {
   var module = angular.module('kibana.panels.filtering', []);
   app.useModule(module);
 
-  module.controller('filtering', function($scope, filterSrv, datasourceSrv, $rootScope, dashboard) {
+  module.controller('filtering', function($scope, datasourceSrv, $rootScope, $timeout) {
 
     $scope.panelMeta = {
       status  : "Stable",
@@ -27,33 +27,41 @@ function (angular, app, _) {
     _.defaults($scope.panel,_d);
 
     $scope.init = function() {
-      $scope.filterSrv = filterSrv;
+        // empty. Don't know if I need the function then.
     };
 
-    $scope.remove = function(filter) {
-      filterSrv.remove(filter);
+    $scope.remove = function( templateParameter ) {
+        this.filter.removeTemplateParameter( templateParameter );
+        
+        // TODO hkraemer: check if this makes sense like this
+        if(!$rootScope.$$phase) {
+            $rootScope.$apply();
+        }
+        $timeout(function(){
+            this.dashboard.refresh();
+        },0);
     };
 
-    $scope.filterOptionSelected = function(filter, option) {
-      filterSrv.filterOptionSelected(filter, option);
-      $scope.applyFilterToOtherFilters(filter);
+    $scope.filterOptionSelected = function( templateParameter, option ) {
+      this.filter.templateOptionSelected(option);
+      this.applyFilterToOtherFilters(templateParameter);
     };
 
     $scope.applyFilterToOtherFilters = function(updatedFilter) {
-      _.each(filterSrv.list, function(filter) {
-        if (filter === updatedFilter) {
+      _.each(this.filter.templateParameters, function( templateParameter ) {
+        if (templateParameter === updatedFilter) {
           return;
         }
-        if (filter.query.indexOf(updatedFilter.name) !== -1) {
-          $scope.applyFilter(filter);
+        if (templateParameter.query.indexOf(updatedFilter.name) !== -1) {
+          $scope.applyFilter(templateParameter);
         }
       });
     };
 
     $scope.applyFilter = function(filter) {
-      var query = filterSrv.applyFilterToTarget(filter.query);
+      var query = this.filter.applyTemplateToTarget(filter.query);
 
-      datasourceSrv.default.metricFindQuery(query)
+      datasourceSrv.default.metricFindQuery($scope, query)
         .then(function (results) {
           filter.editing=undefined;
           filter.options = _.map(results, function(node) {
@@ -69,12 +77,12 @@ function (angular, app, _) {
             filter.options.unshift({text: 'All', value: allExpr});
           }
 
-          filterSrv.filterOptionSelected(filter, filter.options[0]);
+          this.filter.templateOptionSelected(filter, filter.options[0]);
         });
     };
 
     $scope.add = function() {
-      filterSrv.add({
+      this.filter.addTemplateParameter({
         type      : 'filter',
         name      : 'filter name',
         editing   : true,
@@ -83,7 +91,7 @@ function (angular, app, _) {
     };
 
     $scope.refresh = function() {
-      dashboard.refresh();
+      this.dashboard.refresh();
     };
 
     $scope.render = function() {
@@ -91,4 +99,4 @@ function (angular, app, _) {
     };
 
   });
-});
+});

+ 5 - 6
src/app/panels/graphite/module.js

@@ -19,7 +19,6 @@ define([
   'kbn',
   'moment',
   './timeSeries',
-  'services/filterSrv',
   'services/annotationsSrv',
   'services/datasourceSrv',
   'jquery.flot',
@@ -37,7 +36,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
   var module = angular.module('kibana.panels.graphite', []);
   app.useModule(module);
 
-  module.controller('graphite', function($scope, $rootScope, filterSrv, datasourceSrv, $timeout, annotationsSrv) {
+  module.controller('graphite', function($scope, $rootScope, datasourceSrv, $timeout, annotationsSrv) {
 
     $scope.panelMeta = {
       modals : [],
@@ -231,8 +230,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
     };
 
     $scope.updateTimeRange = function () {
-      $scope.range = filterSrv.timeRange();
-      $scope.rangeUnparsed = filterSrv.timeRange(false);
+      $scope.range = this.filter.timeRange();
+      $scope.rangeUnparsed = this.filter.timeRange(false);
       $scope.resolution = Math.ceil($(window).width() * ($scope.panel.span / 12));
       $scope.interval = '10m';
 
@@ -259,9 +258,9 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
         datasource: $scope.panel.datasource
       };
 
-      $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed);
+      $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.filter, $scope.rangeUnparsed);
 
-      return $scope.datasource.query(graphiteQuery)
+      return $scope.datasource.query($scope.filter, graphiteQuery)
         .then($scope.dataHandler)
         .then(null, function(err) {
           $scope.panelMeta.loading = false;

+ 2 - 2
src/app/panels/timepicker/module.html

@@ -17,14 +17,14 @@
 
         <a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" href="" bs-tooltip="time.from.date ? (time.from.date | date:'yyyy-MM-dd HH:mm:ss.sss') + ' <br>to<br>' +(time.to.date | date:'yyyy-MM-dd HH:mm:ss.sss') : 'Click to set a time filter'" data-placement="bottom" ng-click="dismiss();">
 
-          <span ng-show="filterSrv.time">
+          <span ng-show="filter.time">
             <span class="pointer" ng-hide="panel.now">{{time.from.date | date:'MMM d, y HH:mm:ss'}}</span>
             <span class="pointer" ng-show="panel.now">{{time.from.date | moment:'ago'}}</span>
             to
             <span class="pointer" ng-hide="panel.now" >{{time.to.date | date:'MMM d, y HH:mm:ss'}}</span>
             <span class="pointer" ng-show="panel.now">{{time.to.date | moment:'ago'}}</span>
           </span>
-          <span ng-hide="filterSrv.time">Time filter</span>
+          <span ng-hide="filter.time">Time filter</span>
           <span ng-show="dashboard.current.refresh" class="text-warning">refreshed every {{dashboard.current.refresh}} </span>
           <i class="icon-caret-down"></i>
         </a>

+ 5 - 7
src/app/panels/timepicker/module.js

@@ -25,7 +25,7 @@ function (angular, app, _, moment, kbn) {
   var module = angular.module('kibana.panels.timepicker', []);
   app.useModule(module);
 
-  module.controller('timepicker', function($scope, $modal, $q, filterSrv) {
+  module.controller('timepicker', function($scope, $modal, $q) {
     $scope.panelMeta = {
       status  : "Stable",
       description : "A panel for controlling the time range filters. If you have time based data, "+
@@ -44,8 +44,6 @@ function (angular, app, _, moment, kbn) {
 
     _.defaults($scope.panel,_d);
 
-    $scope.filterSrv = filterSrv;
-
     // ng-pattern regexs
     $scope.patterns = {
       date: /^[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/,
@@ -58,9 +56,9 @@ function (angular, app, _, moment, kbn) {
     $scope.$on('refresh', function(){$scope.init();});
 
     $scope.init = function() {
-      var time = filterSrv.timeRange();
+      var time = this.filter.timeRange( true );
       if(time) {
-        $scope.panel.now = filterSrv.timeRange(false).to === "now" ? true : false;
+        $scope.panel.now = this.filter.timeRange(false).to === "now" ? true : false;
         $scope.time = getScopeTimeObj(time.from,time.to);
       }
     };
@@ -135,7 +133,7 @@ function (angular, app, _, moment, kbn) {
       }
 
       // Set the filter
-      $scope.panel.filter_id = filterSrv.setTime(_filter);
+      $scope.panel.filter_id = $scope.filter.setTime(_filter);
 
       // Update our representation
       $scope.time = getScopeTimeObj(time.from,time.to);
@@ -149,7 +147,7 @@ function (angular, app, _, moment, kbn) {
         to: "now"
       };
 
-      filterSrv.setTime(_filter);
+      this.filter.setTime(_filter);
 
       $scope.time = getScopeTimeObj(kbn.parseDate(_filter.from),new Date());
     };

+ 5 - 5
src/app/services/annotationsSrv.js

@@ -28,7 +28,7 @@ define([
       list = [];
     };
 
-    this.getAnnotations = function(rangeUnparsed) {
+    this.getAnnotations = function(filterSrv, rangeUnparsed) {
       if (!annotationPanel.enable) {
         return $q.when(null);
       }
@@ -37,7 +37,7 @@ define([
         return promiseCached;
       }
 
-      var graphiteMetrics = this.getGraphiteMetrics(rangeUnparsed);
+      var graphiteMetrics = this.getGraphiteMetrics(filterSrv, rangeUnparsed);
       var graphiteEvents = this.getGraphiteEvents(rangeUnparsed);
 
       promiseCached = $q.all(graphiteMetrics.concat(graphiteEvents))
@@ -81,7 +81,7 @@ define([
       });
     };
 
-    this.getGraphiteMetrics = function(rangeUnparsed) {
+    this.getGraphiteMetrics = function(filterSrv, rangeUnparsed) {
       var annotations = this.getAnnotationsByType('graphite metric');
       if (annotations.length === 0) {
         return [];
@@ -97,7 +97,7 @@ define([
 
         var receiveFunc = _.partial(receiveGraphiteMetrics, annotation);
 
-        return datasourceSrv.default.query(graphiteQuery)
+        return datasourceSrv.default.query(filterSrv, graphiteQuery)
           .then(receiveFunc)
           .then(null, errorHandler);
       });
@@ -154,4 +154,4 @@ define([
     this.init();
   });
 
-});
+});

+ 0 - 7
src/app/services/dashboard.js

@@ -54,7 +54,6 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
 
     // Store a reference to this
     var self = this;
-    var filterSrv;
 
     this.current = _.clone(_dash);
     this.last = {};
@@ -157,10 +156,6 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
 
       // Set the current dashboard
       self.current = angular.copy(dashboard);
-
-      filterSrv = $injector.get('filterSrv');
-      filterSrv.init();
-
       if(dashboard.refresh) {
         self.set_interval(dashboard.refresh);
       }
@@ -467,8 +462,6 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
         timer.cancel(self.refresh_timer);
       }
     };
-
-
   });
 
 });

+ 4 - 0
src/app/services/dashboard/all.js

@@ -0,0 +1,4 @@
+define([
+  './dashboardKeyBindings',
+],
+function () {});

+ 58 - 0
src/app/services/dashboard/dashboardKeyBindings.js

@@ -0,0 +1,58 @@
+define([
+    'angular',
+    'jquery',
+    'services/all'
+],
+function( angular, $ ) {
+    "use strict";
+
+    var module = angular.module('kibana.services.dashboard');
+
+    module.service( 'dashboardKeybindings', function($rootScope, keyboardManager, dashboard) {
+        this.shortcuts = function() {
+             $rootScope.$on('panel-fullscreen-enter', function() {
+                 $rootScope.fullscreen = true;
+             });
+
+             $rootScope.$on('panel-fullscreen-exit', function() {
+                 $rootScope.fullscreen = false;
+             });
+
+             $rootScope.$on('dashboard-saved', function() {
+                 if ($rootScope.fullscreen) {
+                     $rootScope.$emit('panel-fullscreen-exit');
+                 }
+             });
+
+             keyboardManager.bind('ctrl+f', function(evt) {
+                 $rootScope.$emit('open-search', evt);
+             }, { inputDisabled: true });
+
+             keyboardManager.bind('ctrl+h', function() {
+                 var current = dashboard.current.hideControls;
+                 dashboard.current.hideControls = !current;
+                 dashboard.current.panel_hints = current;
+             }, { inputDisabled: true });
+
+             keyboardManager.bind('ctrl+s', function(evt) {
+                 $rootScope.$emit('save-dashboard', evt);
+             }, { inputDisabled: true });
+
+             keyboardManager.bind('ctrl+r', function() {
+                 dashboard.refresh();
+             }, { inputDisabled: true });
+
+             keyboardManager.bind('ctrl+z', function(evt) {
+                 $rootScope.$emit('zoom-out', evt);
+             }, { inputDisabled: true });
+
+             keyboardManager.bind('esc', function() {
+                 var popups = $('.popover.in');
+                 if (popups.length > 0) {
+                     return;
+                 }
+                 $rootScope.$emit('panel-fullscreen-exit');
+             }, { inputDisabled: true });
+        };
+    });
+});

+ 2 - 2
src/app/services/datasourceSrv.js

@@ -10,7 +10,7 @@ function (angular, _, config) {
 
   var module = angular.module('kibana.services');
 
-  module.service('datasourceSrv', function($q, filterSrv, $http, GraphiteDatasource, InfluxDatasource) {
+  module.service('datasourceSrv', function($q, $http, GraphiteDatasource, InfluxDatasource) {
 
     this.init = function() {
       var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true } );
@@ -48,4 +48,4 @@ function (angular, _, config) {
 
     this.init();
   });
-});
+});

+ 90 - 113
src/app/services/filterSrv.js

@@ -8,125 +8,102 @@ define([
 
   var module = angular.module('kibana.services');
 
-  module.service('filterSrv', function(dashboard, $rootScope, $timeout, $routeParams) {
+  module.factory('filterSrv', function(dashboard, $rootScope, $timeout, $routeParams) {
     // defaults
     var _d = {
-      list: [],
+      templateParameters: [],
       time: {}
     };
 
-    // Save a reference to this
-    var self = this;
+    var result = {
+        _updateTemplateData : function( initial ) {
+            var _templateData = {};
+            _.each(this.templateParameters, function( templateParameter ) {
+                if (initial) {
+                    var urlValue = $routeParams[ templateParameter.name ];
+                    if (urlValue) {
+                        templateParameter.current = { text: urlValue, value: urlValue };
+                    }
+                }
+                if (!templateParameter.current || !templateParameter.current.value) {
+                    return;
+                }
+
+                _templateData[ templateParameter.name ] = templateParameter.current.value;
+            });
+            this._templateData = _templateData;
+        },
+
+        templateOptionSelected : function(templateParameter, option) {
+            templateParameter.current = option;
+            this._updateTemplateData();
+        },
+
+        addTemplateParameter : function( templateParameter ) {
+            this.templateParameters.push( templateParameter );
+            this._updateTemplateData();
+        },
+
+        applyTemplateToTarget : function(target) {
+            if (target.indexOf('[[') === -1) {
+                return target;
+            }
+
+            return _.template(target, this._templateData, this.templateSettings);
+        },
+
+        setTime : function(time) {
+            _.extend(this.time, time);
+            // disable refresh if we have an absolute time
+            if (time.to !== 'now') {
+                this.old_refresh = this.dashboard.refresh;
+                dashboard.set_interval(false);
+                return;
+            }
+
+            if (this.old_refresh && this.old_refresh !== this.dashboard.refresh) {
+                dashboard.set_interval(this.old_refresh);
+                this.old_refresh = null;
+            }
+        },
+
+        timeRange : function(parse) {
+            var _t = this.time;
+            if(_.isUndefined(_t) || _.isUndefined(_t.from)) {
+                return false;
+            }
+            if(parse === false) {
+                return {
+                    from: _t.from,
+                    to: _t.to
+                };
+            } else {
+                var _from = _t.from;
+                var _to = _t.to || new Date();
+
+                return {
+                    from : kbn.parseDate(_from),
+                    to : kbn.parseDate(_to)
+                };
+            }
+        },
+
+        removeTemplateParameter : function( templateParameter ) {
+            this.templateParameters = _.without( this.templateParameters, templateParameter );
+        },
+
+        init : function( dashboard ) {
+            _.defaults(this, _d);
+            this.dashboard = dashboard;
+            this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
+            if( dashboard && dashboard.services && dashboard.services.filter ) {
+                // compatiblity hack
+                this.time = dashboard.services.filter.time;
+            }
 
-    // Call this whenever we need to reload the important stuff
-    this.init = function() {
-      dashboard.current.services.filter = dashboard.current.services.filter || {};
-
-      _.defaults(dashboard.current.services.filter, _d);
-
-      self.list = dashboard.current.services.filter.list;
-      self.time = dashboard.current.services.filter.time;
-
-      self.templateSettings = {
-        interpolate : /\[\[([\s\S]+?)\]\]/g,
-      };
-
-      if (self.list.length) {
-        this._updateTemplateData(true);
-      }
-    };
-
-    this._updateTemplateData = function(initial) {
-      self._filterTemplateData = {};
-
-      _.each(self.list, function(filter) {
-        if (initial) {
-          var urlValue = $routeParams[filter.name];
-          if (urlValue) {
-            filter.current = { text: urlValue, value: urlValue };
-          }
         }
-        if (!filter.current || !filter.current.value) {
-          return;
-        }
-
-        self._filterTemplateData[filter.name] = filter.current.value;
-      });
-    };
-
-    this.filterOptionSelected = function(filter, option) {
-      filter.current = option;
-      this._updateTemplateData();
-      dashboard.refresh();
-    };
-
-    this.add = function(filter) {
-      self.list.push(filter);
-    };
-
-    this.applyFilterToTarget = function(target) {
-      if (target.indexOf('[[') === -1) {
-        return target;
-      }
-
-      return _.template(target, self._filterTemplateData, self.templateSettings);
-    };
-
-    this.remove = function(filter) {
-      self.list = dashboard.current.services.filter.list = _.without(self.list, filter);
-
-      if(!$rootScope.$$phase) {
-        $rootScope.$apply();
-      }
-
-      $timeout(function(){
-        dashboard.refresh();
-      },0);
-    };
-
-    this.setTime = function(time) {
-      _.extend(self.time, time);
-
-      // disable refresh if we have an absolute time
-      if (time.to !== 'now') {
-        self.old_refresh = dashboard.current.refresh;
-        dashboard.set_interval(false);
-      }
-      else if (self.old_refresh && self.old_refresh !== dashboard.current.refresh) {
-        dashboard.set_interval(self.old_refresh);
-        self.old_refresh = null;
-      }
-
-      $timeout(function(){
-        dashboard.refresh();
-      },0);
-    };
-
-    this.timeRange = function(parse) {
-      var _t = self.time;
-
-      if(_.isUndefined(_t)) {
-        return false;
-      }
-      if(parse === false) {
-        return {
-          from: _t.from,
-          to: _t.to
-        };
-      } else {
-        var
-          _from = _t.from,
-          _to = _t.to || new Date();
-
-        return {
-          from : kbn.parseDate(_from),
-          to : kbn.parseDate(_to)
-        };
-      }
-    };
-
-    // Now init
-    self.init();
+    }; 
+    return result;
   });
 
-});
+});

+ 7 - 7
src/app/services/graphite/graphiteDatasource.js

@@ -11,7 +11,7 @@ function (angular, _, $, config, kbn, moment) {
 
   var module = angular.module('kibana.services');
 
-  module.factory('GraphiteDatasource', function(dashboard, $q, filterSrv, $http) {
+  module.factory('GraphiteDatasource', function(dashboard, $q, $http) {
 
     function GraphiteDatasource(datasource) {
       this.type = 'graphite';
@@ -22,7 +22,7 @@ function (angular, _, $, config, kbn, moment) {
       this.render_method = datasource.render_method || 'POST';
     }
 
-    GraphiteDatasource.prototype.query = function(options) {
+    GraphiteDatasource.prototype.query = function(filterSrv, options) {
       try {
         var graphOptions = {
           from: this.translateTime(options.range.from, 'round-down'),
@@ -32,7 +32,7 @@ function (angular, _, $, config, kbn, moment) {
           maxDataPoints: options.maxDataPoints,
         };
 
-        var params = this.buildGraphiteParams(graphOptions);
+        var params = this.buildGraphiteParams(filterSrv, graphOptions);
 
         if (options.format === 'png') {
           return $q.when(this.url + '/render' + '?' + params.join('&'));
@@ -115,10 +115,10 @@ function (angular, _, $, config, kbn, moment) {
       return date.format('HH:mm_YYYYMMDD');
     };
 
-    GraphiteDatasource.prototype.metricFindQuery = function(query) {
+    GraphiteDatasource.prototype.metricFindQuery = function(filterSrv, query) {
       var interpolated;
       try {
-        interpolated = filterSrv.applyFilterToTarget(query);
+        interpolated = filterSrv.applyTemplateToTarget(query);
       }
       catch(err) {
         return $q.reject(err);
@@ -158,7 +158,7 @@ function (angular, _, $, config, kbn, moment) {
       return $http(options);
     };
 
-    GraphiteDatasource.prototype.buildGraphiteParams = function(options) {
+    GraphiteDatasource.prototype.buildGraphiteParams = function(filterSrv, options) {
       var clean_options = [];
       var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
 
@@ -174,7 +174,7 @@ function (angular, _, $, config, kbn, moment) {
         if (key === "targets") {
           _.each(value, function (value) {
             if (!value.hide) {
-              var targetValue = filterSrv.applyFilterToTarget(value.target);
+              var targetValue = filterSrv.applyTemplateToTarget(value.target);
               clean_options.push("target=" + encodeURIComponent(targetValue));
             }
           }, this);

+ 2 - 1
src/app/services/unsavedChangesSrv.js

@@ -56,6 +56,7 @@ function (angular, _, config) {
 
       // ignore timespan changes
       current.services.filter.time = original.services.filter.time = {};
+
       current.refresh = original.refresh;
 
       var currentTimepicker = _.findWhere(current.nav, { type: 'timepicker' });
@@ -99,4 +100,4 @@ function (angular, _, config) {
   }).run(function(unsavedChangesSrv) {
     unsavedChangesSrv.init();
   });
-});
+});

+ 10 - 7
src/test/specs/filterSrv-specs.js

@@ -19,25 +19,28 @@ define([
       _filterSrv = filterSrv;
     }));
 
+    beforeEach(function() {
+        _filterSrv.init( _dashboard.current );
+    });
+
     describe('init', function() {
       beforeEach(function() {
-        _filterSrv.add({ name: 'test', current: { value: 'oogle' } });
-        _filterSrv.init();
+        _filterSrv.addTemplateParameter({ name: 'test', current: { value: 'oogle' } });
       });
 
       it('should initialize template data', function() {
-        var target = _filterSrv.applyFilterToTarget('this.[[test]].filters');
+        var target = _filterSrv.applyTemplateToTarget('this.[[test]].filters');
         expect(target).to.be('this.oogle.filters');
       });
     });
 
-    describe('filterOptionSelected', function() {
+    describe('templateOptionSelected', function() {
       beforeEach(function() {
-        _filterSrv.add({ name: 'test' });
-        _filterSrv.filterOptionSelected(_filterSrv.list[0], { value: 'muuuu' });
+        _filterSrv.addTemplateParameter({ name: 'test' });
+        _filterSrv.templateOptionSelected(_filterSrv.templateParameters[0], { value: 'muuuu' });
       });
       it('should set current value and update template data', function() {
-        var target = _filterSrv.applyFilterToTarget('this.[[test]].filters');
+        var target = _filterSrv.applyTemplateToTarget('this.[[test]].filters');
         expect(target).to.be('this.muuuu.filters');
       });
     });