From 03095dfa7493340876cb05bfa5819f4c78869bfa Mon Sep 17 00:00:00 2001 From: Harald Kraemer Date: Mon, 19 May 2014 15:31:30 +0200 Subject: [PATCH] Changed filterSrv singleton into object in scope. This rework isn't entirely complete here, I haven't checked the tests yet and I didn't really test anything furthermore yet, so bear with this commit breaking things. Besides that, the goal of this commit was to rework the filterSrv singleton into a factory, so we move the filterSrv instance around via the scope. This should be a better solution than the current situation, because services shouldn't contain model data - the scope should. This will eventually straighten out control flow between dashboard, filters and so on, and allow us to leverage angularJS mechanics more. The latter has already started, since I could rework a bit of the existing event infrastructure to watches on times. --- src/app/controllers/dash.js | 15 +- src/app/controllers/dashLoader.js | 6 +- src/app/controllers/graphiteTarget.js | 8 +- src/app/directives/grafanaGraph.js | 11 +- src/app/panels/filtering/module.html | 4 +- src/app/panels/filtering/module.js | 32 +-- src/app/panels/graphite/module.js | 11 +- src/app/panels/timepicker/module.html | 4 +- src/app/panels/timepicker/module.js | 12 +- src/app/services/annotationsSrv.js | 10 +- src/app/services/dashboard.js | 7 - src/app/services/datasourceSrv.js | 4 +- src/app/services/filterSrv.js | 186 ++++++++---------- .../services/graphite/graphiteDatasource.js | 10 +- src/app/services/unsavedChangesSrv.js | 3 +- 15 files changed, 156 insertions(+), 167 deletions(-) diff --git a/src/app/controllers/dash.js b/src/app/controllers/dash.js index 1038378b1b3..e4d6af888da 100644 --- a/src/app/controllers/dash.js +++ b/src/app/controllers/dash.js @@ -31,7 +31,7 @@ function (angular, $, config, _) { var module = angular.module('kibana.controllers'); module.controller('DashCtrl', function( - $scope, $rootScope, ejsResource, dashboard, dashboardKeybindings, + $scope, $rootScope, $timeout, ejsResource, dashboard, filterSrv, dashboardKeybindings, alertSrv, panelMove, keyboardManager, grafanaVersion) { $scope.requiredElasticSearchVersion = ">=0.90.3"; @@ -57,6 +57,19 @@ function (angular, $, config, _) { $scope.dashboard = dashboard; $scope.dashAlerts = alertSrv; + $scope.filter = filterSrv; + console.log( "dash controller -> init -> current dashboard", dashboard.current ); + $scope.filter.init( dashboard.current ); + + $scope.$watch('dashboard.current', function(newValue, oldValue) { + $scope.filter.init( newValue ); + }); + + console.log( "Scope I watch on", $scope ); + $scope.$watch('filter.time', function(newValue, oldValue) { + console.log( "Hai" ); + $scope.dashboard.refresh(); + }, true); // Clear existing alerts alertSrv.clearAll(); diff --git a/src/app/controllers/dashLoader.js b/src/app/controllers/dashLoader.js index 7f9dc9232dd..fe5ed2a9c66 100644 --- a/src/app/controllers/dashLoader.js +++ b/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(), }); diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js index a7224b2ced6..5608df6d178 100644 --- a/src/app/controllers/graphiteTarget.js +++ b/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, $http) { $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.filterSrv, path) .then(function(segments) { if (segments.length === 0) { $scope.segments = $scope.segments.splice(0, fromIndex); @@ -157,13 +157,13 @@ function (angular, _, config, gfunc, Parser) { var query = index === 0 ? '*' : getSegmentPathUpTo(index) + '.*'; - return $scope.datasource.metricFindQuery(query) + return $scope.datasource.metricFindQuery($scope.filterSrv, query) .then(function(segments) { _.each(segments, function(segment) { segment.html = segment.val = segment.text; }); - _.each(filterSrv.list, function(filter) { + _.each($scope.filter.list, function(filter) { segments.unshift({ type: 'template', html: '[[' + filter.name + ']]', diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index d5a44598ad0..883b8ab7655 100644 --- a/src/app/directives/grafanaGraph.js +++ b/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: '
', @@ -387,9 +387,12 @@ 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() { + console.log( "Scope I call filter.setTime on", scope ); + scope.filter.setTime({ + from : moment.utc(ranges.xaxis.from).toDate(), + to : moment.utc(ranges.xaxis.to).toDate(), + }); }); }); } diff --git a/src/app/panels/filtering/module.html b/src/app/panels/filtering/module.html index a360161aa83..950390b8107 100644 --- a/src/app/panels/filtering/module.html +++ b/src/app/panels/filtering/module.html @@ -2,7 +2,7 @@
-
+
@@ -47,4 +47,4 @@
-
\ No newline at end of file +
diff --git a/src/app/panels/filtering/module.js b/src/app/panels/filtering/module.js index c7c475fbd60..c4353202d17 100644 --- a/src/app/panels/filtering/module.js +++ b/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, dashboard) { $scope.panelMeta = { status : "Stable", @@ -28,19 +28,29 @@ function (angular, app, _) { $scope.init = function() { $scope.filterSrv = filterSrv; + console.log( "Filtering panel " + $scope.dashboard ); + $scope.filterSrv.init( $scope.dashboard ); }; $scope.remove = function(filter) { - filterSrv.remove(filter); + this.filter.removeFilter(filter); + + // 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); + this.filter.filterOptionSelected(option); + this.applyFilterToOtherFilters(filter); }; $scope.applyFilterToOtherFilters = function(updatedFilter) { - _.each(filterSrv.list, function(filter) { + _.each(this.filter.list, function(filter) { if (filter === updatedFilter) { return; } @@ -51,9 +61,9 @@ function (angular, app, _) { }; $scope.applyFilter = function(filter) { - var query = filterSrv.applyFilterToTarget(filter.query); + var query = this.filter.applyFilterToTarget(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 +79,12 @@ function (angular, app, _) { filter.options.unshift({text: 'All', value: allExpr}); } - filterSrv.filterOptionSelected(filter, filter.options[0]); + this.filterSrv.filterOptionSelected(filter, filter.options[0]); }); }; $scope.add = function() { - filterSrv.add({ + this.filter.add({ type : 'filter', name : 'filter name', editing : true, @@ -83,7 +93,7 @@ function (angular, app, _) { }; $scope.refresh = function() { - dashboard.refresh(); + this.dashboard.refresh(); }; $scope.render = function() { @@ -91,4 +101,4 @@ function (angular, app, _) { }; }); -}); \ No newline at end of file +}); diff --git a/src/app/panels/graphite/module.js b/src/app/panels/graphite/module.js index 270be72e2b6..3a35fe9be69 100644 --- a/src/app/panels/graphite/module.js +++ b/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.filterSrv, $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; diff --git a/src/app/panels/timepicker/module.html b/src/app/panels/timepicker/module.html index f0975d371ae..ea1759d2141 100644 --- a/src/app/panels/timepicker/module.html +++ b/src/app/panels/timepicker/module.html @@ -17,14 +17,14 @@ - + {{time.from.date | date:'MMM d, y HH:mm:ss'}} {{time.from.date | moment:'ago'}} to {{time.to.date | date:'MMM d, y HH:mm:ss'}} {{time.to.date | moment:'ago'}} - Time filter + Time filter refreshed every {{dashboard.current.refresh}} diff --git a/src/app/panels/timepicker/module.js b/src/app/panels/timepicker/module.js index 198239f6a0d..a16ebc132d5 100644 --- a/src/app/panels/timepicker/module.js +++ b/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()); }; diff --git a/src/app/services/annotationsSrv.js b/src/app/services/annotationsSrv.js index dceb28eea10..459b5ff2816 100644 --- a/src/app/services/annotationsSrv.js +++ b/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(); }); -}); \ No newline at end of file +}); diff --git a/src/app/services/dashboard.js b/src/app/services/dashboard.js index 6c4cfd67e34..4f0ca8cee7c 100644 --- a/src/app/services/dashboard.js +++ b/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); } }; - - }); }); diff --git a/src/app/services/datasourceSrv.js b/src/app/services/datasourceSrv.js index 77f0f9f1142..1f6f882ff5a 100644 --- a/src/app/services/datasourceSrv.js +++ b/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(); }); -}); \ No newline at end of file +}); diff --git a/src/app/services/filterSrv.js b/src/app/services/filterSrv.js index 7126a14d5e6..2efd54c4e52 100644 --- a/src/app/services/filterSrv.js +++ b/src/app/services/filterSrv.js @@ -8,125 +8,99 @@ 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: [], time: {} }; - // Save a reference to this - var self = this; + var result = { + _updateTemplateData : function( initial ) { + this._filterTemplateData = {}; + _.each(this.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; + } - // Call this whenever we need to reload the important stuff - this.init = function() { - dashboard.current.services.filter = dashboard.current.services.filter || {}; + this._filterTemplateData[filter.name] = filter.current.value; + }); + }, - _.defaults(dashboard.current.services.filter, _d); + filterOptionSelected : function(option) { + this.current = option; + this._updateTemplateData(); + }, - self.list = dashboard.current.services.filter.list; - self.time = dashboard.current.services.filter.time; + add : function(filter) { + this.list.push(filter); + }, - self.templateSettings = { - interpolate : /\[\[([\s\S]+?)\]\]/g, - }; + applyFilterToTarget : function(target) { + if (target.indexOf('[[') === -1) { + return target; + } - if (self.list.length) { - this._updateTemplateData(true); - } - }; + return _.template(target, this._filterTemplateData, this.templateSettings); + }, - this._updateTemplateData = function(initial) { - self._filterTemplateData = {}; + 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) + }; + } + }, + + removeFilter : function( filter, dashboard ) { + this.list = _.without(this.list, filter); + }, + 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; + } - _.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; }); -}); \ No newline at end of file +}); diff --git a/src/app/services/graphite/graphiteDatasource.js b/src/app/services/graphite/graphiteDatasource.js index d48fdf7dd12..46f90909f08 100644 --- a/src/app/services/graphite/graphiteDatasource.js +++ b/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,7 +115,7 @@ 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); @@ -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']; diff --git a/src/app/services/unsavedChangesSrv.js b/src/app/services/unsavedChangesSrv.js index a0d2a079d34..5d90052b322 100644 --- a/src/app/services/unsavedChangesSrv.js +++ b/src/app/services/unsavedChangesSrv.js @@ -55,7 +55,6 @@ function (angular, _, config) { var original = dashboard.original; // ignore timespan changes - current.services.filter.time = original.services.filter.time = {}; current.refresh = original.refresh; var currentTimepicker = _.findWhere(current.nav, { type: 'timepicker' }); @@ -99,4 +98,4 @@ function (angular, _, config) { }).run(function(unsavedChangesSrv) { unsavedChangesSrv.init(); }); -}); \ No newline at end of file +});