소스 검색

tech: ran pretttier on all typescript files

Torkel Ödegaard 8 년 전
부모
커밋
fadfc665e4
100개의 변경된 파일4185개의 추가작업 그리고 3389개의 파일을 삭제
  1. 100 78
      public/app/app.ts
  2. 1 1
      public/app/core/app_events.ts
  3. 26 24
      public/app/core/components/code_editor/code_editor.ts
  4. 7 6
      public/app/core/components/colorpicker/spectrum_picker.ts
  5. 9 10
      public/app/core/components/dashboard_selector.ts
  6. 50 33
      public/app/core/components/form_dropdown/form_dropdown.ts
  7. 6 6
      public/app/core/components/gf_page.ts
  8. 116 50
      public/app/core/components/grafana_app.ts
  9. 44 38
      public/app/core/components/help/help.ts
  10. 21 22
      public/app/core/components/info_popover.ts
  11. 30 20
      public/app/core/components/json_explorer/helpers.ts
  12. 96 62
      public/app/core/components/json_explorer/json_explorer.ts
  13. 21 20
      public/app/core/components/jsontree/jsontree.ts
  14. 26 23
      public/app/core/components/layout_selector/layout_selector.ts
  15. 77 42
      public/app/core/components/manage_dashboards/manage_dashboards.ts
  16. 13 15
      public/app/core/components/navbar/navbar.ts
  17. 11 9
      public/app/core/components/org_switcher.ts
  18. 19 20
      public/app/core/components/query_part/query_part.ts
  19. 47 35
      public/app/core/components/query_part/query_part_editor.ts
  20. 7 9
      public/app/core/components/scroll/scroll.ts
  21. 48 28
      public/app/core/components/search/search.ts
  22. 15 15
      public/app/core/components/search/search_results.ts
  23. 41 25
      public/app/core/components/sidemenu/sidemenu.ts
  24. 6 7
      public/app/core/components/switch.ts
  25. 21 14
      public/app/core/components/team_picker.ts
  26. 21 14
      public/app/core/components/user_picker.ts
  27. 33 33
      public/app/core/config.ts
  28. 1 2
      public/app/core/constants.ts
  29. 6 7
      public/app/core/controllers/error_ctrl.ts
  30. 25 15
      public/app/core/controllers/inspect_ctrl.ts
  31. 22 22
      public/app/core/controllers/invited_ctrl.ts
  32. 6 6
      public/app/core/controllers/json_editor_ctrl.ts
  33. 23 22
      public/app/core/controllers/login_ctrl.ts
  34. 22 17
      public/app/core/controllers/reset_password_ctrl.ts
  35. 23 25
      public/app/core/controllers/signup_ctrl.ts
  36. 47 47
      public/app/core/core.ts
  37. 8 10
      public/app/core/directives/array_join.ts
  38. 16 17
      public/app/core/directives/diff-view.ts
  39. 18 14
      public/app/core/directives/give_focus.ts
  40. 19 17
      public/app/core/directives/ng_model_on_blur.ts
  41. 12 8
      public/app/core/directives/rebuild_on_change.ts
  42. 102 44
      public/app/core/directives/tags.ts
  43. 20 22
      public/app/core/filters/filters.ts
  44. 24 20
      public/app/core/live/live_srv.ts
  45. 2 6
      public/app/core/mod_defs.d.ts
  46. 6 6
      public/app/core/nav_model_srv.ts
  47. 56 30
      public/app/core/profiler.ts
  48. 15 11
      public/app/core/routes/bundle_loader.ts
  49. 29 26
      public/app/core/routes/dashboard_loaders.ts
  50. 286 264
      public/app/core/routes/routes.ts
  51. 55 33
      public/app/core/services/alert_srv.ts
  52. 16 14
      public/app/core/services/analytics.ts
  53. 138 115
      public/app/core/services/backend_srv.ts
  54. 14 11
      public/app/core/services/context_srv.ts
  55. 27 21
      public/app/core/services/dynamic_directive_srv.ts
  56. 14 12
      public/app/core/services/global_event_srv.ts
  57. 4 4
      public/app/core/services/impression_srv.ts
  58. 66 65
      public/app/core/services/keybindingSrv.ts
  59. 60 40
      public/app/core/services/ng_react.ts
  60. 8 9
      public/app/core/services/popover_srv.ts
  61. 47 41
      public/app/core/services/search_srv.ts
  62. 4 5
      public/app/core/services/timer.ts
  63. 7 8
      public/app/core/services/util_srv.ts
  64. 28 18
      public/app/core/specs/backend_srv_specs.ts
  65. 71 53
      public/app/core/specs/datemath.jest.ts
  66. 20 20
      public/app/core/specs/emitter.jest.ts
  67. 15 15
      public/app/core/specs/flatten.jest.ts
  68. 9 9
      public/app/core/specs/global_event_srv.jest.ts
  69. 206 198
      public/app/core/specs/kbn.jest.ts
  70. 122 154
      public/app/core/specs/manage_dashboards.jest.ts
  71. 23 16
      public/app/core/specs/org_switcher.jest.ts
  72. 74 62
      public/app/core/specs/rangeutil.jest.ts
  73. 61 68
      public/app/core/specs/search.jest.ts
  74. 40 36
      public/app/core/specs/search_results.jest.ts
  75. 91 87
      public/app/core/specs/search_srv.jest.ts
  76. 8 12
      public/app/core/specs/store.jest.ts
  77. 8 11
      public/app/core/specs/table_model.jest.ts
  78. 107 97
      public/app/core/specs/time_series.jest.ts
  79. 46 35
      public/app/core/specs/value_select_dropdown_specs.ts
  80. 1 3
      public/app/core/store.ts
  81. 1 2
      public/app/core/table_model.ts
  82. 81 41
      public/app/core/time_series2.ts
  83. 61 12
      public/app/core/utils/colors.ts
  84. 14 13
      public/app/core/utils/css_loader.ts
  85. 24 17
      public/app/core/utils/datemath.ts
  86. 69 55
      public/app/core/utils/file_export.ts
  87. 7 2
      public/app/core/utils/flatten.ts
  88. 493 392
      public/app/core/utils/kbn.ts
  89. 6 2
      public/app/core/utils/model_utils.ts
  90. 13 11
      public/app/core/utils/outline.ts
  91. 73 63
      public/app/core/utils/rangeutil.ts
  92. 12 5
      public/app/core/utils/react2angular.ts
  93. 23 18
      public/app/core/utils/ticks.ts
  94. 6 2
      public/app/core/utils/version.ts
  95. 14 15
      public/app/features/admin/admin.ts
  96. 22 14
      public/app/features/admin/admin_edit_org_ctrl.ts
  97. 63 36
      public/app/features/admin/admin_edit_user_ctrl.ts
  98. 13 12
      public/app/features/admin/admin_list_orgs_ctrl.ts
  99. 25 19
      public/app/features/admin/admin_list_users_ctrl.ts
  100. 75 69
      public/app/features/alerting/alert_def.ts

+ 100 - 78
public/app/app.ts

@@ -1,32 +1,32 @@
-import 'babel-polyfill';
-import 'file-saver';
-import 'lodash';
-import 'jquery';
-import 'angular';
-import 'angular-route';
-import 'angular-sanitize';
-import 'angular-native-dragdrop';
-import 'angular-bindonce';
-import 'react';
-import 'react-dom';
-
-import 'vendor/bootstrap/bootstrap';
-import 'vendor/angular-ui/ui-bootstrap-tpls';
-import 'vendor/angular-other/angular-strap';
-
-import $ from 'jquery';
-import angular from 'angular';
-import config from 'app/core/config';
-import _ from 'lodash';
-import moment from 'moment';
+import "babel-polyfill";
+import "file-saver";
+import "lodash";
+import "jquery";
+import "angular";
+import "angular-route";
+import "angular-sanitize";
+import "angular-native-dragdrop";
+import "angular-bindonce";
+import "react";
+import "react-dom";
+
+import "vendor/bootstrap/bootstrap";
+import "vendor/angular-ui/ui-bootstrap-tpls";
+import "vendor/angular-other/angular-strap";
+
+import $ from "jquery";
+import angular from "angular";
+import config from "app/core/config";
+import _ from "lodash";
+import moment from "moment";
 
 // add move to lodash for backward compatabiltiy
-_.move = function (array, fromIndex, toIndex) {
+_.move = function(array, fromIndex, toIndex) {
   array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
   return array;
 };
 
-import {coreModule, registerAngularDirectives} from './core/core';
+import { coreModule, registerAngularDirectives } from "./core/core";
 
 export class GrafanaApp {
   registerFunctions: any;
@@ -50,58 +50,78 @@ export class GrafanaApp {
   }
 
   init() {
-    var app = angular.module('grafana', []);
+    var app = angular.module("grafana", []);
 
     moment.locale(config.bootData.user.locale);
 
-    app.config(($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $httpProvider, $provide) => {
-      // pre assing bindings before constructor calls
-      $compileProvider.preAssignBindingsEnabled(true);
-
-      if (config.buildInfo.env !== 'development') {
-        $compileProvider.debugInfoEnabled(false);
-      }
-
-      $httpProvider.useApplyAsync(true);
-
-      this.registerFunctions.controller = $controllerProvider.register;
-      this.registerFunctions.directive  = $compileProvider.directive;
-      this.registerFunctions.factory    = $provide.factory;
-      this.registerFunctions.service    = $provide.service;
-      this.registerFunctions.filter     = $filterProvider.register;
-
-      $provide.decorator("$http", ["$delegate", "$templateCache", function($delegate, $templateCache) {
-        var get = $delegate.get;
-        $delegate.get = function(url, config) {
-          if (url.match(/\.html$/)) {
-            // some template's already exist in the cache
-            if (!$templateCache.get(url)) {
-              url += "?v=" + new Date().getTime();
-            }
+    app.config(
+      (
+        $locationProvider,
+        $controllerProvider,
+        $compileProvider,
+        $filterProvider,
+        $httpProvider,
+        $provide
+      ) => {
+        // pre assing bindings before constructor calls
+        $compileProvider.preAssignBindingsEnabled(true);
+
+        if (config.buildInfo.env !== "development") {
+          $compileProvider.debugInfoEnabled(false);
+        }
+
+        $httpProvider.useApplyAsync(true);
+
+        this.registerFunctions.controller = $controllerProvider.register;
+        this.registerFunctions.directive = $compileProvider.directive;
+        this.registerFunctions.factory = $provide.factory;
+        this.registerFunctions.service = $provide.service;
+        this.registerFunctions.filter = $filterProvider.register;
+
+        $provide.decorator("$http", [
+          "$delegate",
+          "$templateCache",
+          function($delegate, $templateCache) {
+            var get = $delegate.get;
+            $delegate.get = function(url, config) {
+              if (url.match(/\.html$/)) {
+                // some template's already exist in the cache
+                if (!$templateCache.get(url)) {
+                  url += "?v=" + new Date().getTime();
+                }
+              }
+              return get(url, config);
+            };
+            return $delegate;
           }
-          return get(url, config);
-        };
-        return $delegate;
-      }]);
-    });
+        ]);
+      }
+    );
 
     this.ngModuleDependencies = [
-      'grafana.core',
-      'ngRoute',
-      'ngSanitize',
-      '$strap.directives',
-      'ang-drag-drop',
-      'grafana',
-      'pasvaz.bindonce',
-      'ui.bootstrap',
-      'ui.bootstrap.tpls',
-      'react'
+      "grafana.core",
+      "ngRoute",
+      "ngSanitize",
+      "$strap.directives",
+      "ang-drag-drop",
+      "grafana",
+      "pasvaz.bindonce",
+      "ui.bootstrap",
+      "ui.bootstrap.tpls",
+      "react"
     ];
 
-    var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
+    var module_types = [
+      "controllers",
+      "directives",
+      "factories",
+      "services",
+      "filters",
+      "routes"
+    ];
 
     _.each(module_types, type => {
-      var moduleName = 'grafana.' + type;
+      var moduleName = "grafana." + type;
       this.useModule(angular.module(moduleName, []));
     });
 
@@ -111,22 +131,24 @@ export class GrafanaApp {
     // register react angular wrappers
     registerAngularDirectives();
 
-    var preBootRequires = [System.import('app/features/all')];
+    var preBootRequires = [System.import("app/features/all")];
 
-    Promise.all(preBootRequires).then(() => {
-      // disable tool tip animation
-      $.fn.tooltip.defaults.animation = false;
-      // bootstrap the app
-      angular.bootstrap(document, this.ngModuleDependencies).invoke(() => {
-        _.each(this.preBootModules, module => {
-          _.extend(module, this.registerFunctions);
-        });
+    Promise.all(preBootRequires)
+      .then(() => {
+        // disable tool tip animation
+        $.fn.tooltip.defaults.animation = false;
+        // bootstrap the app
+        angular.bootstrap(document, this.ngModuleDependencies).invoke(() => {
+          _.each(this.preBootModules, module => {
+            _.extend(module, this.registerFunctions);
+          });
 
-        this.preBootModules = null;
+          this.preBootModules = null;
+        });
+      })
+      .catch(function(err) {
+        console.log("Application boot failed:", err);
       });
-    }).catch(function(err) {
-      console.log('Application boot failed:', err);
-    });
   }
 }
 

+ 1 - 1
public/app/core/app_events.ts

@@ -1,4 +1,4 @@
-import {Emitter} from './utils/emitter';
+import { Emitter } from "./utils/emitter";
 
 var appEvents = new Emitter();
 export default appEvents;

+ 26 - 24
public/app/core/components/code_editor/code_editor.ts

@@ -26,20 +26,20 @@
  * Ctrl-Enter (Command-Enter): run onChange() function
  */
 
-import coreModule from 'app/core/core_module';
-import config from 'app/core/config';
-import ace from 'brace';
-import './theme-grafana-dark';
-import 'brace/ext/language_tools';
-import 'brace/theme/textmate';
-import 'brace/mode/text';
-import 'brace/snippets/text';
-import 'brace/mode/sql';
-import 'brace/snippets/sql';
-import 'brace/mode/markdown';
-import 'brace/snippets/markdown';
-import 'brace/mode/json';
-import 'brace/snippets/json';
+import coreModule from "app/core/core_module";
+import config from "app/core/config";
+import ace from "brace";
+import "./theme-grafana-dark";
+import "brace/ext/language_tools";
+import "brace/theme/textmate";
+import "brace/mode/text";
+import "brace/snippets/text";
+import "brace/mode/sql";
+import "brace/snippets/sql";
+import "brace/mode/markdown";
+import "brace/snippets/markdown";
+import "brace/mode/json";
+import "brace/snippets/json";
 
 const DEFAULT_THEME_DARK = "ace/theme/grafana-dark";
 const DEFAULT_THEME_LIGHT = "ace/theme/textmate";
@@ -56,7 +56,9 @@ function link(scope, elem, attrs) {
   let maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
   let showGutter = attrs.showGutter !== undefined;
   let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
-  let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS;
+  let behavioursEnabled = attrs.behavioursEnabled
+    ? attrs.behavioursEnabled === "true"
+    : DEFAULT_BEHAVIOURS;
 
   // Initialize editor
   let aceElem = elem.get(0);
@@ -88,10 +90,10 @@ function link(scope, elem, attrs) {
   // Add classes
   elem.addClass("gf-code-editor");
   let textarea = elem.find("textarea");
-  textarea.addClass('gf-form-input');
+  textarea.addClass("gf-form-input");
 
   if (scope.codeEditorFocus) {
-    setTimeout(function () {
+    setTimeout(function() {
       textarea.focus();
       var domEl = textarea[0];
       if (domEl.setSelectionRange) {
@@ -102,7 +104,7 @@ function link(scope, elem, attrs) {
   }
 
   // Event handlers
-  editorSession.on('change', (e) => {
+  editorSession.on("change", e => {
     scope.$apply(() => {
       let newValue = codeEditor.getValue();
       scope.content = newValue;
@@ -110,7 +112,7 @@ function link(scope, elem, attrs) {
   });
 
   // Sync with outer scope - update editor content if model has been changed from outside of directive.
-  scope.$watch('content', (newValue, oldValue) => {
+  scope.$watch("content", (newValue, oldValue) => {
     let editorValue = codeEditor.getValue();
     if (newValue !== editorValue && newValue !== oldValue) {
       scope.$$postDigest(function() {
@@ -119,7 +121,7 @@ function link(scope, elem, attrs) {
     }
   });
 
-  codeEditor.on('blur', () => {
+  codeEditor.on("blur", () => {
     scope.onChange();
   });
 
@@ -129,8 +131,8 @@ function link(scope, elem, attrs) {
 
   // Keybindings
   codeEditor.commands.addCommand({
-    name: 'executeQuery',
-    bindKey: {win: 'Ctrl-Enter', mac: 'Command-Enter'},
+    name: "executeQuery",
+    bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" },
     exec: () => {
       scope.onChange();
     }
@@ -172,7 +174,7 @@ function link(scope, elem, attrs) {
 
 export function codeEditorDirective() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: editorTemplate,
     scope: {
       content: "=",
@@ -185,4 +187,4 @@ export function codeEditorDirective() {
   };
 }
 
-coreModule.directive('codeEditor', codeEditorDirective);
+coreModule.directive("codeEditor", codeEditorDirective);

+ 7 - 6
public/app/core/components/colorpicker/spectrum_picker.ts

@@ -3,22 +3,23 @@
  * Allows remaining <spectrum-picker> untouched in outdated plugins.
  * Technically, it's just a wrapper for react component with two-way data binding support.
  */
-import coreModule from '../../core_module';
+import coreModule from "../../core_module";
 
 /** @ngInject */
 export function spectrumPicker() {
   return {
-    restrict: 'E',
-    require: 'ngModel',
+    restrict: "E",
+    require: "ngModel",
     scope: true,
     replace: true,
-    template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
+    template:
+      '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
     link: function(scope, element, attrs, ngModel) {
       scope.ngModel = ngModel;
-      scope.onColorChange = (color) => {
+      scope.onColorChange = color => {
         ngModel.$setViewValue(color);
       };
     }
   };
 }
-coreModule.directive('spectrumPicker', spectrumPicker);
+coreModule.directive("spectrumPicker", spectrumPicker);

+ 9 - 10
public/app/core/components/dashboard_selector.ts

@@ -1,4 +1,4 @@
-import coreModule from 'app/core/core_module';
+import coreModule from "app/core/core_module";
 
 var template = `
 <select class="gf-form-input" ng-model="ctrl.model" ng-options="f.value as f.text for f in ctrl.options"></select>
@@ -9,15 +9,14 @@ export class DashboardSelectorCtrl {
   options: any;
 
   /** @ngInject */
-  constructor(private backendSrv) {
-  }
+  constructor(private backendSrv) {}
 
   $onInit() {
-    this.options = [{value: 0, text: 'Default'}];
+    this.options = [{ value: 0, text: "Default" }];
 
-    return this.backendSrv.search({starred: true}).then(res => {
+    return this.backendSrv.search({ starred: true }).then(res => {
       res.forEach(dash => {
-        this.options.push({value: dash.id, text: dash.title});
+        this.options.push({ value: dash.id, text: dash.title });
       });
     });
   }
@@ -25,15 +24,15 @@ export class DashboardSelectorCtrl {
 
 export function dashboardSelector() {
   return {
-    restrict: 'E',
+    restrict: "E",
     controller: DashboardSelectorCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     template: template,
     scope: {
-      model: '='
+      model: "="
     }
   };
 }
 
-coreModule.directive('dashboardSelector', dashboardSelector);
+coreModule.directive("dashboardSelector", dashboardSelector);

+ 50 - 33
public/app/core/components/form_dropdown/form_dropdown.ts

@@ -1,11 +1,15 @@
-import _ from 'lodash';
-import $ from 'jquery';
-import coreModule from '../../core_module';
+import _ from "lodash";
+import $ from "jquery";
+import coreModule from "../../core_module";
 
 function typeaheadMatcher(item) {
   var str = this.query;
-  if (str[0] === '/') { str = str.substring(1); }
-  if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
+  if (str[0] === "/") {
+    str = str.substring(1);
+  }
+  if (str[str.length - 1] === "/") {
+    str = str.substring(0, str.length - 1);
+  }
   return item.toLowerCase().match(str.toLowerCase());
 }
 
@@ -28,9 +32,15 @@ export class FormDropdownCtrl {
   lookupText: boolean;
 
   /** @ngInject **/
-  constructor(private $scope, $element, private $sce, private templateSrv, private $q) {
-    this.inputElement = $element.find('input').first();
-    this.linkElement = $element.find('a').first();
+  constructor(
+    private $scope,
+    $element,
+    private $sce,
+    private templateSrv,
+    private $q
+  ) {
+    this.inputElement = $element.find("input").first();
+    this.linkElement = $element.find("a").first();
     this.linkMode = true;
     this.cancelBlur = null;
 
@@ -38,25 +48,26 @@ export class FormDropdownCtrl {
     $scope.$watch("ctrl.model", this.modelChanged.bind(this));
 
     if (this.labelMode) {
-      this.cssClasses = 'gf-form-label ' + this.cssClass;
+      this.cssClasses = "gf-form-label " + this.cssClass;
     } else {
-      this.cssClasses = 'gf-form-input gf-form-input--dropdown ' + this.cssClass;
+      this.cssClasses =
+        "gf-form-input gf-form-input--dropdown " + this.cssClass;
     }
 
-    this.inputElement.attr('data-provide', 'typeahead');
+    this.inputElement.attr("data-provide", "typeahead");
     this.inputElement.typeahead({
       source: this.typeaheadSource.bind(this),
       minLength: 0,
       items: 10000,
       updater: this.typeaheadUpdater.bind(this),
-      matcher: typeaheadMatcher,
+      matcher: typeaheadMatcher
     });
 
     // modify typeahead lookup
     // this = typeahead
-    var typeahead = this.inputElement.data('typeahead');
-    typeahead.lookup = function () {
-      this.query = this.$element.val() || '';
+    var typeahead = this.inputElement.data("typeahead");
+    typeahead.lookup = function() {
+      this.query = this.$element.val() || "";
       var items = this.source(this.query, $.proxy(this.process, this));
       return items ? this.process(items) : items;
     };
@@ -80,7 +91,7 @@ export class FormDropdownCtrl {
   }
 
   getOptionsInternal(query) {
-    var result = this.getOptions({$query: query});
+    var result = this.getOptions({ $query: query });
     if (this.isPromiseLike(result)) {
       return result;
     }
@@ -88,7 +99,7 @@ export class FormDropdownCtrl {
   }
 
   isPromiseLike(obj) {
-    return obj && (typeof obj.then === 'function');
+    return obj && typeof obj.then === "function";
   }
 
   modelChanged() {
@@ -98,7 +109,7 @@ export class FormDropdownCtrl {
       // if we have text use it
       if (this.lookupText) {
         this.getOptionsInternal("").then(options => {
-          var item = _.find(options, {value: this.model});
+          var item = _.find(options, { value: this.model });
           this.updateDisplay(item ? item.text : this.model);
         });
       } else {
@@ -140,7 +151,9 @@ export class FormDropdownCtrl {
   }
 
   switchToLink(fromClick) {
-    if (this.linkMode && !fromClick) { return; }
+    if (this.linkMode && !fromClick) {
+      return;
+    }
 
     clearTimeout(this.cancelBlur);
     this.cancelBlur = null;
@@ -159,12 +172,12 @@ export class FormDropdownCtrl {
   updateValue(text) {
     text = _.unescape(text);
 
-    if (text === '' || this.text === text) {
+    if (text === "" || this.text === text) {
       return;
     }
 
     this.$scope.$apply(() => {
-      var option = _.find(this.optionCache, {text: text});
+      var option = _.find(this.optionCache, { text: text });
 
       if (option) {
         if (_.isObject(this.model)) {
@@ -186,20 +199,24 @@ export class FormDropdownCtrl {
       // property is synced with outerscope
       this.$scope.$$postDigest(() => {
         this.$scope.$apply(() => {
-          this.onChange({$option: option});
+          this.onChange({ $option: option });
         });
       });
-
     });
   }
 
   updateDisplay(text) {
     this.text = text;
-    this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text));
+    this.display = this.$sce.trustAsHtml(
+      this.templateSrv.highlightVariablesAsHtml(text)
+    );
   }
 
   open() {
-    this.inputElement.css('width', (Math.max(this.linkElement.width(), 80) + 16) + 'px');
+    this.inputElement.css(
+      "width",
+      Math.max(this.linkElement.width(), 80) + 16 + "px"
+    );
 
     this.inputElement.show();
     this.inputElement.focus();
@@ -207,15 +224,15 @@ export class FormDropdownCtrl {
     this.linkElement.hide();
     this.linkMode = false;
 
-    var typeahead = this.inputElement.data('typeahead');
+    var typeahead = this.inputElement.data("typeahead");
     if (typeahead) {
-      this.inputElement.val('');
+      this.inputElement.val("");
       typeahead.lookup();
     }
   }
 }
 
-const template =  `
+const template = `
 <input type="text"
   data-provide="typeahead"
   class="gf-form-input"
@@ -232,11 +249,11 @@ const template =  `
 
 export function formDropdownDirective() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     controller: FormDropdownCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
       model: "=",
       getOptions: "&",
@@ -244,9 +261,9 @@ export function formDropdownDirective() {
       cssClass: "@",
       allowCustom: "@",
       labelMode: "@",
-      lookupText: "@",
-    },
+      lookupText: "@"
+    }
   };
 }
 
-coreModule.directive('gfFormDropdown', formDropdownDirective);
+coreModule.directive("gfFormDropdown", formDropdownDirective);

+ 6 - 6
public/app/core/components/gf_page.ts

@@ -1,6 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import coreModule from 'app/core/core_module';
+import coreModule from "app/core/core_module";
 
 const template = `
 <div class="scroll-canvas">
@@ -24,14 +24,14 @@ const template = `
 
 export function gfPageDirective() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     scope: {
-      "model": "=",
+      model: "="
     },
     transclude: {
-      'header': '?gfPageHeader',
-      'body': 'gfPageBody',
+      header: "?gfPageHeader",
+      body: "gfPageBody"
     },
     link: function(scope, elem, attrs) {
       console.log(scope);
@@ -39,4 +39,4 @@ export function gfPageDirective() {
   };
 }
 
-coreModule.directive('gfPage', gfPageDirective);
+coreModule.directive("gfPage", gfPageDirective);

+ 116 - 50
public/app/core/components/grafana_app.ts

@@ -1,17 +1,23 @@
-import config from 'app/core/config';
-import _ from 'lodash';
-import $ from 'jquery';
+import config from "app/core/config";
+import _ from "lodash";
+import $ from "jquery";
 
-import coreModule from 'app/core/core_module';
-import {profiler} from 'app/core/profiler';
-import appEvents from 'app/core/app_events';
-import Drop from 'tether-drop';
+import coreModule from "app/core/core_module";
+import { profiler } from "app/core/profiler";
+import appEvents from "app/core/app_events";
+import Drop from "tether-drop";
 
 export class GrafanaCtrl {
-
   /** @ngInject */
-  constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, globalEventSrv) {
-
+  constructor(
+    $scope,
+    alertSrv,
+    utilSrv,
+    $rootScope,
+    $controller,
+    contextSrv,
+    globalEventSrv
+  ) {
     $scope.init = function() {
       $scope.contextSrv = contextSrv;
 
@@ -28,19 +34,19 @@ export class GrafanaCtrl {
 
     $scope.initDashboard = function(dashboardData, viewScope) {
       $scope.appEvent("dashboard-fetch-end", dashboardData);
-      $controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
+      $controller("DashboardCtrl", { $scope: viewScope }).init(dashboardData);
     };
 
     $rootScope.onAppEvent = function(name, callback, localScope) {
       var unbind = $rootScope.$on(name, callback);
       var callerScope = this;
       if (callerScope.$id === 1 && !localScope) {
-        console.log('warning rootScope onAppEvent called without localscope');
+        console.log("warning rootScope onAppEvent called without localscope");
       }
       if (localScope) {
         callerScope = localScope;
       }
-      callerScope.$on('$destroy', unbind);
+      callerScope.$on("$destroy", unbind);
     };
 
     $rootScope.appEvent = function(name, payload) {
@@ -49,13 +55,62 @@ export class GrafanaCtrl {
     };
 
     $rootScope.colors = [
-      "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
-      "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
-      "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
-      "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
-      "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
-      "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
-      "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
+      "#7EB26D",
+      "#EAB839",
+      "#6ED0E0",
+      "#EF843C",
+      "#E24D42",
+      "#1F78C1",
+      "#BA43A9",
+      "#705DA0",
+      "#508642",
+      "#CCA300",
+      "#447EBC",
+      "#C15C17",
+      "#890F02",
+      "#0A437C",
+      "#6D1F62",
+      "#584477",
+      "#B7DBAB",
+      "#F4D598",
+      "#70DBED",
+      "#F9BA8F",
+      "#F29191",
+      "#82B5D8",
+      "#E5A8E2",
+      "#AEA2E0",
+      "#629E51",
+      "#E5AC0E",
+      "#64B0C8",
+      "#E0752D",
+      "#BF1B00",
+      "#0A50A1",
+      "#962D82",
+      "#614D93",
+      "#9AC48A",
+      "#F2C96D",
+      "#65C5DB",
+      "#F9934E",
+      "#EA6460",
+      "#5195CE",
+      "#D683CE",
+      "#806EB7",
+      "#3F6833",
+      "#967302",
+      "#2F575E",
+      "#99440A",
+      "#58140C",
+      "#052B51",
+      "#511749",
+      "#3F2B5B",
+      "#E0F9D7",
+      "#FCEACA",
+      "#CFFAFF",
+      "#F9E2D2",
+      "#FCE2DE",
+      "#BADFF4",
+      "#F9D9F9",
+      "#DEDAF7"
     ];
 
     $scope.init();
@@ -63,30 +118,35 @@ export class GrafanaCtrl {
 }
 
 /** @ngInject */
-export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope) {
+export function grafanaAppDirective(
+  playlistSrv,
+  contextSrv,
+  $timeout,
+  $rootScope
+) {
   return {
-    restrict: 'E',
+    restrict: "E",
     controller: GrafanaCtrl,
     link: (scope, elem) => {
       var sidemenuOpen;
-      var body = $('body');
+      var body = $("body");
 
       // see https://github.com/zenorocha/clipboard.js/issues/155
       $.fn.modal.Constructor.prototype.enforceFocus = function() {};
 
       sidemenuOpen = scope.contextSrv.sidemenu;
-      body.toggleClass('sidemenu-open', sidemenuOpen);
+      body.toggleClass("sidemenu-open", sidemenuOpen);
 
-      appEvents.on('toggle-sidemenu', () => {
-        body.toggleClass('sidemenu-open');
+      appEvents.on("toggle-sidemenu", () => {
+        body.toggleClass("sidemenu-open");
       });
 
-      appEvents.on('toggle-sidemenu-mobile', () => {
-        body.toggleClass('sidemenu-open--xs');
+      appEvents.on("toggle-sidemenu-mobile", () => {
+        body.toggleClass("sidemenu-open--xs");
       });
 
-      appEvents.on('toggle-sidemenu-hidden', () => {
-        body.toggleClass('sidemenu-hidden');
+      appEvents.on("toggle-sidemenu-hidden", () => {
+        body.toggleClass("sidemenu-hidden");
       });
 
       // tooltip removal fix
@@ -105,13 +165,13 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
         }
 
         // clear body class sidemenu states
-        body.removeClass('sidemenu-open--xs');
+        body.removeClass("sidemenu-open--xs");
 
         $("#tooltip, .tooltip").remove();
 
         // check for kiosk url param
         if (data.params.kiosk) {
-          appEvents.emit('toggle-kiosk-mode');
+          appEvents.emit("toggle-kiosk-mode");
         }
 
         // close all drops
@@ -121,8 +181,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
       });
 
       // handle kiosk mode
-      appEvents.on('toggle-kiosk-mode', () => {
-        body.toggleClass('page-kiosk-mode');
+      appEvents.on("toggle-kiosk-mode", () => {
+        body.toggleClass("page-kiosk-mode");
       });
 
       // handle in active view state class
@@ -136,17 +196,17 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
           return;
         }
         // only go to activity low mode on dashboard page
-        if (!body.hasClass('page-dashboard')) {
+        if (!body.hasClass("page-dashboard")) {
           return;
         }
 
-        if ((new Date().getTime() - lastActivity) > inActiveTimeLimit) {
+        if (new Date().getTime() - lastActivity > inActiveTimeLimit) {
           activeUser = false;
-          body.addClass('user-activity-low');
+          body.addClass("user-activity-low");
           // hide sidemenu
           if (sidemenuOpen) {
             sidemenuHidden = true;
-            body.removeClass('sidemenu-open');
+            body.removeClass("sidemenu-open");
             $timeout(function() {
               $rootScope.$broadcast("render");
             }, 100);
@@ -158,12 +218,12 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
         lastActivity = new Date().getTime();
         if (!activeUser) {
           activeUser = true;
-          body.removeClass('user-activity-low');
+          body.removeClass("user-activity-low");
 
           // restore sidemenu
           if (sidemenuHidden) {
             sidemenuHidden = false;
-            body.addClass('sidemenu-open');
+            body.addClass("sidemenu-open");
             $timeout(function() {
               $rootScope.$broadcast("render");
             }, 100);
@@ -175,12 +235,12 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
       body.mousemove(userActivityDetected);
       body.keydown(userActivityDetected);
       // treat tab change as activity
-      document.addEventListener('visibilitychange', userActivityDetected);
+      document.addEventListener("visibilitychange", userActivityDetected);
 
       // check every 2 seconds
       setInterval(checkForInActiveUser, 2000);
 
-      appEvents.on('toggle-view-mode', () => {
+      appEvents.on("toggle-view-mode", () => {
         lastActivity = 0;
         checkForInActiveUser();
       });
@@ -194,7 +254,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
 
         // for stuff that animates, slides out etc, clicking it needs to
         // hide it right away
-        var clickAutoHide = target.closest('[data-click-hide]');
+        var clickAutoHide = target.closest("[data-click-hide]");
         if (clickAutoHide.length) {
           var clickAutoHideParent = clickAutoHide.parent();
           clickAutoHide.detach();
@@ -203,22 +263,28 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
           }, 100);
         }
 
-        if (target.parents('.navbar-buttons--playlist').length === 0) {
+        if (target.parents(".navbar-buttons--playlist").length === 0) {
           playlistSrv.stop();
         }
 
         // hide search
-        if (body.find('.search-container').length > 0) {
-          if (target.parents('.search-results-container, .search-field-wrapper').length === 0) {
+        if (body.find(".search-container").length > 0) {
+          if (
+            target.parents(".search-results-container, .search-field-wrapper")
+              .length === 0
+          ) {
             scope.$apply(function() {
-              scope.appEvent('hide-dash-search');
+              scope.appEvent("hide-dash-search");
             });
           }
         }
 
         // hide popovers
-        var popover = elem.find('.popover');
-        if (popover.length > 0 && target.parents('.graph-legend').length === 0) {
+        var popover = elem.find(".popover");
+        if (
+          popover.length > 0 &&
+          target.parents(".graph-legend").length === 0
+        ) {
           popover.hide();
         }
       });
@@ -226,4 +292,4 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
   };
 }
 
-coreModule.directive('grafanaApp', grafanaAppDirective);
+coreModule.directive("grafanaApp", grafanaAppDirective);

+ 44 - 38
public/app/core/components/help/help.ts

@@ -1,7 +1,7 @@
 ///<reference path="../../../headers/common.d.ts" />
 
-import coreModule from '../../core_module';
-import appEvents from 'app/core/app_events';
+import coreModule from "../../core_module";
+import appEvents from "app/core/app_events";
 
 export class HelpCtrl {
   tabIndex: any;
@@ -11,58 +11,64 @@ export class HelpCtrl {
   constructor() {
     this.tabIndex = 0;
     this.shortcuts = {
-      'Global': [
-        {keys: ['g', 'h'], description: 'Go to Home Dashboard'},
-        {keys: ['g', 'p'], description: 'Go to Profile'},
-        {keys: ['s', 'o'], description: 'Open search'},
-        {keys: ['s', 's'], description: 'Open search with starred filter'},
-        {keys: ['s', 't'], description: 'Open search in tags view'},
-        {keys: ['esc'], description: 'Exit edit/setting views'},
+      Global: [
+        { keys: ["g", "h"], description: "Go to Home Dashboard" },
+        { keys: ["g", "p"], description: "Go to Profile" },
+        { keys: ["s", "o"], description: "Open search" },
+        { keys: ["s", "s"], description: "Open search with starred filter" },
+        { keys: ["s", "t"], description: "Open search in tags view" },
+        { keys: ["esc"], description: "Exit edit/setting views" }
       ],
-      'Dashboard': [
-        {keys: ['mod+s'], description: 'Save dashboard'},
-        {keys: ['mod+h'], description: 'Hide row controls'},
-        {keys: ['d', 'r'], description: 'Refresh all panels'},
-        {keys: ['d', 's'], description: 'Dashboard settings'},
-        {keys: ['d', 'v'], description: 'Toggle in-active / view mode'},
-        {keys: ['d', 'k'], description: 'Toggle kiosk mode (hides top nav)'},
-        {keys: ['d', 'E'], description: 'Expand all rows'},
-        {keys: ['d', 'C'], description: 'Collapse all rows'},
-        {keys: ['mod+o'], description: 'Toggle shared graph crosshair'},
+      Dashboard: [
+        { keys: ["mod+s"], description: "Save dashboard" },
+        { keys: ["mod+h"], description: "Hide row controls" },
+        { keys: ["d", "r"], description: "Refresh all panels" },
+        { keys: ["d", "s"], description: "Dashboard settings" },
+        { keys: ["d", "v"], description: "Toggle in-active / view mode" },
+        { keys: ["d", "k"], description: "Toggle kiosk mode (hides top nav)" },
+        { keys: ["d", "E"], description: "Expand all rows" },
+        { keys: ["d", "C"], description: "Collapse all rows" },
+        { keys: ["mod+o"], description: "Toggle shared graph crosshair" }
       ],
-      'Focused Panel': [
-        {keys: ['e'], description: 'Toggle panel edit view'},
-        {keys: ['v'], description: 'Toggle panel fullscreen view'},
-        {keys: ['p', 's'], description: 'Open Panel Share Modal'},
-        {keys: ['p', 'r'], description: 'Remove Panel'},
+      "Focused Panel": [
+        { keys: ["e"], description: "Toggle panel edit view" },
+        { keys: ["v"], description: "Toggle panel fullscreen view" },
+        { keys: ["p", "s"], description: "Open Panel Share Modal" },
+        { keys: ["p", "r"], description: "Remove Panel" }
       ],
-      'Focused Row': [
-        {keys: ['r', 'c'], description: 'Collapse Row'},
-        {keys: ['r', 'r'], description: 'Remove Row'},
-      ],
-      'Time Range': [
-        {keys: ['t', 'z'], description: 'Zoom out time range'},
-        {keys: ['t', '<i class="fa fa-long-arrow-left"></i>'], description: 'Move time range back'},
-        {keys: ['t', '<i class="fa fa-long-arrow-right"></i>'], description: 'Move time range forward'},
+      "Focused Row": [
+        { keys: ["r", "c"], description: "Collapse Row" },
+        { keys: ["r", "r"], description: "Remove Row" }
       ],
+      "Time Range": [
+        { keys: ["t", "z"], description: "Zoom out time range" },
+        {
+          keys: ["t", '<i class="fa fa-long-arrow-left"></i>'],
+          description: "Move time range back"
+        },
+        {
+          keys: ["t", '<i class="fa fa-long-arrow-right"></i>'],
+          description: "Move time range forward"
+        }
+      ]
     };
   }
 
   dismiss() {
-    appEvents.emit('hide-modal');
+    appEvents.emit("hide-modal");
   }
 }
 
 export function helpModal() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/help/help.html',
+    restrict: "E",
+    templateUrl: "public/app/core/components/help/help.html",
     controller: HelpCtrl,
     bindToController: true,
     transclude: true,
-    controllerAs: 'ctrl',
-    scope: {},
+    controllerAs: "ctrl",
+    scope: {}
   };
 }
 
-coreModule.directive('helpModal', helpModal);
+coreModule.directive("helpModal", helpModal);

+ 21 - 22
public/app/core/components/info_popover.ts

@@ -1,35 +1,35 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import Drop from 'tether-drop';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import Drop from "tether-drop";
 
 export function infoPopover() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: '<i class="fa fa-info-circle"></i>',
     transclude: true,
     link: function(scope, elem, attrs, ctrl, transclude) {
-      var offset = attrs.offset || '0 -10px';
-      var position = attrs.position || 'right middle';
-      var classes = 'drop-help drop-hide-out-of-bounds';
-      var openOn = 'hover';
+      var offset = attrs.offset || "0 -10px";
+      var position = attrs.position || "right middle";
+      var classes = "drop-help drop-hide-out-of-bounds";
+      var openOn = "hover";
 
-      elem.addClass('gf-form-help-icon');
+      elem.addClass("gf-form-help-icon");
 
       if (attrs.wide) {
-        classes += ' drop-wide';
+        classes += " drop-wide";
       }
 
       if (attrs.mode) {
-        elem.addClass('gf-form-help-icon--' + attrs.mode);
+        elem.addClass("gf-form-help-icon--" + attrs.mode);
       }
 
       transclude(function(clone, newScope) {
         var content = document.createElement("div");
-        content.className = 'markdown-html';
+        content.className = "markdown-html";
 
-        _.each(clone, (node) => {
+        _.each(clone, node => {
           content.appendChild(node);
         });
 
@@ -43,23 +43,22 @@ export function infoPopover() {
           tetherOptions: {
             offset: offset,
             constraints: [
-                {
-                  to: 'window',
-                  attachment: 'together',
-                  pin: true
-                }
-              ],
+              {
+                to: "window",
+                attachment: "together",
+                pin: true
+              }
+            ]
           }
         });
 
-        var unbind = scope.$on('$destroy', function() {
+        var unbind = scope.$on("$destroy", function() {
           drop.destroy();
           unbind();
         });
-
       });
     }
   };
 }
 
-coreModule.directive('infoPopover', infoPopover);
+coreModule.directive("infoPopover", infoPopover);

+ 30 - 20
public/app/core/components/json_explorer/helpers.ts

@@ -5,7 +5,7 @@
  * Escapes `"` charachters from string
 */
 function escapeString(str: string): string {
-  return str.replace('"', '\"');
+  return str.replace('"', '"');
 }
 
 /*
@@ -13,7 +13,7 @@ function escapeString(str: string): string {
 */
 export function isObject(value: any): boolean {
   var type = typeof value;
-  return !!value && (type === 'object');
+  return !!value && type === "object";
 }
 
 /*
@@ -23,21 +23,21 @@ export function isObject(value: any): boolean {
 */
 export function getObjectName(object: Object): string {
   if (object === undefined) {
-    return '';
+    return "";
   }
   if (object === null) {
-    return 'Object';
+    return "Object";
   }
-  if (typeof object === 'object' && !object.constructor) {
-      return 'Object';
+  if (typeof object === "object" && !object.constructor) {
+    return "Object";
   }
 
   const funcNameRegex = /function ([^(]*)/;
-  const results = (funcNameRegex).exec((object).constructor.toString());
+  const results = funcNameRegex.exec(object.constructor.toString());
   if (results && results.length > 1) {
     return results[1];
   } else {
-    return '';
+    return "";
   }
 }
 
@@ -45,27 +45,33 @@ export function getObjectName(object: Object): string {
  * Gets type of an object. Returns "null" for null objects
 */
 export function getType(object: Object): string {
-  if (object === null) { return 'null'; }
+  if (object === null) {
+    return "null";
+  }
   return typeof object;
 }
 
 /*
  * Generates inline preview for a JavaScript object based on a value
 */
-export function getValuePreview (object: Object, value: string): string {
+export function getValuePreview(object: Object, value: string): string {
   var type = getType(object);
 
-  if (type === 'null' || type === 'undefined') { return type; }
+  if (type === "null" || type === "undefined") {
+    return type;
+  }
 
-  if (type === 'string') {
+  if (type === "string") {
     value = '"' + escapeString(value) + '"';
   }
-  if (type === 'function') {
-
+  if (type === "function") {
     // Remove content of the function
-    return object.toString()
-        .replace(/[\r\n]/g, '')
-        .replace(/\{.*\}/, '') + '{…}';
+    return (
+      object
+        .toString()
+        .replace(/[\r\n]/g, "")
+        .replace(/\{.*\}/, "") + "{…}"
+    );
   }
   return value;
 }
@@ -74,11 +80,11 @@ export function getValuePreview (object: Object, value: string): string {
  * Generates inline preview for a JavaScript object
 */
 export function getPreview(object: string): string {
-  let value = '';
+  let value = "";
   if (isObject(object)) {
     value = getObjectName(object);
     if (Array.isArray(object)) {
-      value += '[' + object.length + ']';
+      value += "[" + object.length + "]";
     }
   } else {
     value = getValuePreview(object, object);
@@ -97,7 +103,11 @@ export function cssClass(className: string): string {
   * Creates a new DOM element wiht given type and class
   * TODO: move me to helpers
 */
-export function createElement(type: string, className?: string, content?: Element|string): Element {
+export function createElement(
+  type: string,
+  className?: string,
+  content?: Element | string
+): Element {
   const el = document.createElement(type);
   if (className) {
     el.classList.add(cssClass(className));

+ 96 - 62
public/app/core/components/json_explorer/json_explorer.ts

@@ -8,9 +8,9 @@ import {
   getValuePreview,
   cssClass,
   createElement
-} from './helpers';
+} from "./helpers";
 
-import _ from 'lodash';
+import _ from "lodash";
 
 const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/;
 const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/;
@@ -19,7 +19,12 @@ const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
 // When toggleing, don't animated removal or addition of more than a few items
 const MAX_ANIMATED_TOGGLE_ITEMS = 10;
 
-const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; };
+const requestAnimationFrame =
+  window.requestAnimationFrame ||
+  function(cb: () => void) {
+    cb();
+    return 0;
+  };
 
 export interface JsonExplorerConfig {
   animateOpen?: boolean;
@@ -33,15 +38,13 @@ const _defaultConfig: JsonExplorerConfig = {
   theme: null
 };
 
-
 /**
  * @class JsonExplorer
  *
  * JsonExplorer allows you to render JSON objects in HTML with a
  * **collapsible** navigation.
-*/
+ */
 export class JsonExplorer {
-
   // Hold the open state after the toggler is used
   private _isOpen: boolean = null;
 
@@ -77,9 +80,13 @@ export class JsonExplorer {
    *
    * @param {string} [key=undefined] The key that this object in it's parent
    * context
-  */
-  constructor(public json: any, private open = 1, private config: JsonExplorerConfig = _defaultConfig, private key?: string) {
-  }
+   */
+  constructor(
+    public json: any,
+    private open = 1,
+    private config: JsonExplorerConfig = _defaultConfig,
+    private key?: string
+  ) {}
 
   /*
    * is formatter open?
@@ -103,17 +110,19 @@ export class JsonExplorer {
    * is this a date string?
   */
   private get isDate(): boolean {
-    return (this.type === 'string') &&
+    return (
+      this.type === "string" &&
       (DATE_STRING_REGEX.test(this.json) ||
-      JSON_DATE_REGEX.test(this.json) ||
-      PARTIAL_DATE_REGEX.test(this.json));
+        JSON_DATE_REGEX.test(this.json) ||
+        PARTIAL_DATE_REGEX.test(this.json))
+    );
   }
 
   /*
    * is this a URL string?
   */
   private get isUrl(): boolean {
-    return this.type === 'string' && (this.json.indexOf('http') === 0);
+    return this.type === "string" && this.json.indexOf("http") === 0;
   }
 
   /*
@@ -142,7 +151,9 @@ export class JsonExplorer {
    * is this an empty object or array?
   */
   private get isEmpty(): boolean {
-    return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
+    return (
+      this.isEmptyObject || (this.keys && !this.keys.length && this.isArray)
+    );
   }
 
   /*
@@ -150,7 +161,7 @@ export class JsonExplorer {
    * This means that the formatter was called as a sub formatter of a parent formatter
   */
   private get hasKey(): boolean {
-    return typeof this.key !== 'undefined';
+    return typeof this.key !== "undefined";
   }
 
   /*
@@ -174,7 +185,7 @@ export class JsonExplorer {
   */
   private get keys(): string[] {
     if (this.isObject) {
-      return Object.keys(this.json).map((key)=> key ? key : '""');
+      return Object.keys(this.json).map(key => (key ? key : '""'));
     } else {
       return [];
     }
@@ -183,7 +194,7 @@ export class JsonExplorer {
   /**
    * Toggles `isOpen` state
    *
-  */
+   */
   toggleOpen() {
     this.isOpen = !this.isOpen;
 
@@ -193,58 +204,65 @@ export class JsonExplorer {
       } else {
         this.removeChildren(this.config.animateClose);
       }
-      this.element.classList.toggle(cssClass('open'));
+      this.element.classList.toggle(cssClass("open"));
     }
   }
 
   /**
-  * Open all children up to a certain depth.
-  * Allows actions such as expand all/collapse all
-  *
-  */
+   * Open all children up to a certain depth.
+   * Allows actions such as expand all/collapse all
+   *
+   */
   openAtDepth(depth = 1) {
     if (depth < 0) {
       return;
     }
 
     this.open = depth;
-    this.isOpen = (depth !== 0);
+    this.isOpen = depth !== 0;
 
     if (this.element) {
       this.removeChildren(false);
 
       if (depth === 0) {
-        this.element.classList.remove(cssClass('open'));
+        this.element.classList.remove(cssClass("open"));
       } else {
         this.appendChildren(this.config.animateOpen);
-        this.element.classList.add(cssClass('open'));
+        this.element.classList.add(cssClass("open"));
       }
     }
   }
 
   isNumberArray() {
-    return (this.json.length > 0 && this.json.length < 4) &&
-      (_.isNumber(this.json[0]) || _.isNumber(this.json[1]));
+    return (
+      this.json.length > 0 &&
+      this.json.length < 4 &&
+      (_.isNumber(this.json[0]) || _.isNumber(this.json[1]))
+    );
   }
 
   renderArray() {
-    const arrayWrapperSpan = createElement('span');
-    arrayWrapperSpan.appendChild(createElement('span', 'bracket', '['));
+    const arrayWrapperSpan = createElement("span");
+    arrayWrapperSpan.appendChild(createElement("span", "bracket", "["));
 
     // some pretty handling of number arrays
     if (this.isNumberArray()) {
       this.json.forEach((val, index) => {
         if (index > 0) {
-          arrayWrapperSpan.appendChild(createElement('span', 'array-comma', ','));
+          arrayWrapperSpan.appendChild(
+            createElement("span", "array-comma", ",")
+          );
         }
-        arrayWrapperSpan.appendChild(createElement('span', 'number', val));
+        arrayWrapperSpan.appendChild(createElement("span", "number", val));
       });
       this.skipChildren = true;
     } else {
-      arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length)));
+      arrayWrapperSpan.appendChild(
+        createElement("span", "number", this.json.length)
+      );
     }
 
-    arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
+    arrayWrapperSpan.appendChild(createElement("span", "bracket", "]"));
     return arrayWrapperSpan;
   }
 
@@ -255,11 +273,11 @@ export class JsonExplorer {
    */
   render(skipRoot = false): HTMLDivElement {
     // construct the root element and assign it to this.element
-    this.element = createElement('div', 'row');
+    this.element = createElement("div", "row");
 
     // construct the toggler link
-    const togglerLink = createElement('a', 'toggler-link');
-    const togglerIcon = createElement('span', 'toggler');
+    const togglerLink = createElement("a", "toggler-link");
+    const togglerIcon = createElement("span", "toggler");
 
     // if this is an object we need a wrapper span (toggler)
     if (this.isObject) {
@@ -268,19 +286,23 @@ export class JsonExplorer {
 
     // if this is child of a parent formatter we need to append the key
     if (this.hasKey) {
-      togglerLink.appendChild(createElement('span', 'key', `${this.key}:`));
+      togglerLink.appendChild(createElement("span", "key", `${this.key}:`));
     }
 
     // Value for objects and arrays
     if (this.isObject) {
       // construct the value holder element
-      const value = createElement('span', 'value');
+      const value = createElement("span", "value");
 
       // we need a wrapper span for objects
-      const objectWrapperSpan = createElement('span');
+      const objectWrapperSpan = createElement("span");
 
       // get constructor name and append it to wrapper span
-      var constructorName = createElement('span', 'constructor-name', this.constructorName);
+      var constructorName = createElement(
+        "span",
+        "constructor-name",
+        this.constructorName
+      );
       objectWrapperSpan.appendChild(constructorName);
 
       // if it's an array append the array specific elements like brackets and length
@@ -294,18 +316,17 @@ export class JsonExplorer {
       togglerLink.appendChild(value);
       // Primitive values
     } else {
-
       // make a value holder element
-      const value = this.isUrl ? createElement('a') : createElement('span');
+      const value = this.isUrl ? createElement("a") : createElement("span");
 
       // add type and other type related CSS classes
       value.classList.add(cssClass(this.type));
       if (this.isDate) {
-        value.classList.add(cssClass('date'));
+        value.classList.add(cssClass("date"));
       }
       if (this.isUrl) {
-        value.classList.add(cssClass('url'));
-        value.setAttribute('href', this.json);
+        value.classList.add(cssClass("url"));
+        value.setAttribute("href", this.json);
       }
 
       // Append value content to value element
@@ -317,17 +338,17 @@ export class JsonExplorer {
     }
 
     // construct a children element
-    const children = createElement('div', 'children');
+    const children = createElement("div", "children");
 
     // set CSS classes for children
     if (this.isObject) {
-      children.classList.add(cssClass('object'));
+      children.classList.add(cssClass("object"));
     }
     if (this.isArray) {
-      children.classList.add(cssClass('array'));
+      children.classList.add(cssClass("array"));
     }
     if (this.isEmpty) {
-      children.classList.add(cssClass('empty'));
+      children.classList.add(cssClass("empty"));
     }
 
     // set CSS classes for root element
@@ -335,7 +356,7 @@ export class JsonExplorer {
       this.element.classList.add(cssClass(this.config.theme));
     }
     if (this.isOpen) {
-      this.element.classList.add(cssClass('open'));
+      this.element.classList.add(cssClass("open"));
     }
 
     // append toggler and children elements to root element
@@ -357,7 +378,7 @@ export class JsonExplorer {
 
     // add event listener for toggling
     if (this.isObject) {
-      togglerLink.addEventListener('click', this.toggleOpen.bind(this));
+      togglerLink.addEventListener("click", this.toggleOpen.bind(this));
     }
 
     return this.element as HTMLDivElement;
@@ -366,17 +387,24 @@ export class JsonExplorer {
   /**
    * Appends all the children to children element
    * Animated option is used when user triggers this via a click
-  */
+   */
   appendChildren(animated = false) {
-    const children = this.element.querySelector(`div.${cssClass('children')}`);
+    const children = this.element.querySelector(`div.${cssClass("children")}`);
 
-    if (!children || this.isEmpty) { return; }
+    if (!children || this.isEmpty) {
+      return;
+    }
 
     if (animated) {
       let index = 0;
-      const addAChild = ()=> {
+      const addAChild = () => {
         const key = this.keys[index];
-        const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
+        const formatter = new JsonExplorer(
+          this.json[key],
+          this.open - 1,
+          this.config,
+          key
+        );
         children.appendChild(formatter.render());
 
         index += 1;
@@ -391,10 +419,14 @@ export class JsonExplorer {
       };
 
       requestAnimationFrame(addAChild);
-
     } else {
       this.keys.forEach(key => {
-        const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
+        const formatter = new JsonExplorer(
+          this.json[key],
+          this.open - 1,
+          this.config,
+          key
+        );
         children.appendChild(formatter.render());
       });
     }
@@ -403,13 +435,15 @@ export class JsonExplorer {
   /**
    * Removes all the children from children element
    * Animated option is used when user triggers this via a click
-  */
+   */
   removeChildren(animated = false) {
-    const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement;
+    const childrenElement = this.element.querySelector(
+      `div.${cssClass("children")}`
+    ) as HTMLDivElement;
 
     if (animated) {
       let childrenRemoved = 0;
-      const removeAChild = ()=> {
+      const removeAChild = () => {
         if (childrenElement && childrenElement.children.length) {
           childrenElement.removeChild(childrenElement.children[0]);
           childrenRemoved += 1;
@@ -423,7 +457,7 @@ export class JsonExplorer {
       requestAnimationFrame(removeAChild);
     } else {
       if (childrenElement) {
-        childrenElement.innerHTML = '';
+        childrenElement.innerHTML = "";
       }
     }
   }

+ 21 - 20
public/app/core/components/jsontree/jsontree.ts

@@ -1,22 +1,23 @@
-import coreModule from 'app/core/core_module';
-import {JsonExplorer} from '../json_explorer/json_explorer';
+import coreModule from "app/core/core_module";
+import { JsonExplorer } from "../json_explorer/json_explorer";
 
-coreModule.directive('jsonTree', [function jsonTreeDirective() {
-  return{
-    restrict: 'E',
-    scope: {
-      object: '=',
-      startExpanded: '@',
-      rootName: '@',
-    },
-    link: function(scope, elem) {
+coreModule.directive("jsonTree", [
+  function jsonTreeDirective() {
+    return {
+      restrict: "E",
+      scope: {
+        object: "=",
+        startExpanded: "@",
+        rootName: "@"
+      },
+      link: function(scope, elem) {
+        var jsonExp = new JsonExplorer(scope.object, 3, {
+          animateOpen: true
+        });
 
-      var jsonExp = new JsonExplorer(scope.object, 3, {
-        animateOpen: true
-      });
-
-      const html = jsonExp.render(true);
-      elem.html(html);
-    }
-  };
-}]);
+        const html = jsonExp.render(true);
+        elem.html(html);
+      }
+    };
+  }
+]);

+ 26 - 23
public/app/core/components/layout_selector/layout_selector.ts

@@ -1,5 +1,5 @@
-import store from 'app/core/store';
-import coreModule from 'app/core/core_module';
+import store from "app/core/store";
+import coreModule from "app/core/core_module";
 
 var template = `
 <div class="layout-selector">
@@ -17,53 +17,56 @@ export class LayoutSelectorCtrl {
 
   /** @ngInject **/
   constructor(private $rootScope) {
-    this.mode = store.get('grafana.list.layout.mode') || 'grid';
+    this.mode = store.get("grafana.list.layout.mode") || "grid";
   }
 
   listView() {
-    this.mode = 'list';
-    store.set('grafana.list.layout.mode', 'list');
-    this.$rootScope.appEvent('layout-mode-changed', 'list');
+    this.mode = "list";
+    store.set("grafana.list.layout.mode", "list");
+    this.$rootScope.appEvent("layout-mode-changed", "list");
   }
 
   gridView() {
-    this.mode = 'grid';
-    store.set('grafana.list.layout.mode', 'grid');
-    this.$rootScope.appEvent('layout-mode-changed', 'grid');
+    this.mode = "grid";
+    store.set("grafana.list.layout.mode", "grid");
+    this.$rootScope.appEvent("layout-mode-changed", "grid");
   }
-
 }
 
 /** @ngInject **/
 export function layoutSelector() {
   return {
-    restrict: 'E',
+    restrict: "E",
     controller: LayoutSelectorCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {},
-    template: template,
+    template: template
   };
 }
 
 /** @ngInject **/
 export function layoutMode($rootScope) {
   return {
-    restrict: 'A',
+    restrict: "A",
     scope: {},
     link: function(scope, elem) {
-      var layout = store.get('grafana.list.layout.mode') || 'grid';
-      var className = 'card-list-layout-' + layout;
+      var layout = store.get("grafana.list.layout.mode") || "grid";
+      var className = "card-list-layout-" + layout;
       elem.addClass(className);
 
-      $rootScope.onAppEvent('layout-mode-changed', (evt, newLayout) => {
-        elem.removeClass(className);
-        className = 'card-list-layout-' + newLayout;
-        elem.addClass(className);
-      }, scope);
+      $rootScope.onAppEvent(
+        "layout-mode-changed",
+        (evt, newLayout) => {
+          elem.removeClass(className);
+          className = "card-list-layout-" + newLayout;
+          elem.addClass(className);
+        },
+        scope
+      );
     }
   };
 }
 
-coreModule.directive('layoutSelector', layoutSelector);
-coreModule.directive('layoutMode', layoutMode);
+coreModule.directive("layoutSelector", layoutSelector);
+coreModule.directive("layoutMode", layoutMode);

+ 77 - 42
public/app/core/components/manage_dashboards/manage_dashboards.ts

@@ -1,7 +1,7 @@
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
-import { SearchSrv } from 'app/core/services/search_srv';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import appEvents from "app/core/app_events";
+import { SearchSrv } from "app/core/services/search_srv";
 
 export class ManageDashboardsCtrl {
   public sections: any[];
@@ -13,13 +13,24 @@ export class ManageDashboardsCtrl {
   canMove = false;
   hasFilters = false;
   selectAllChecked = false;
-  starredFilterOptions = [{ text: 'Filter by Starred', disabled: true }, { text: 'Yes' }, { text: 'No' }];
+  starredFilterOptions = [
+    { text: "Filter by Starred", disabled: true },
+    { text: "Yes" },
+    { text: "No" }
+  ];
   selectedStarredFilter: any;
   folderId?: number;
 
   /** @ngInject */
   constructor(private backendSrv, navModelSrv, private searchSrv: SearchSrv) {
-    this.query = { query: '', mode: 'tree', tag: [], starred: false, skipRecent: true, skipStarred: true };
+    this.query = {
+      query: "",
+      mode: "tree",
+      tag: [],
+      starred: false,
+      skipRecent: true,
+      skipStarred: true
+    };
 
     if (this.folderId) {
       this.query.folderIds = [this.folderId];
@@ -33,7 +44,7 @@ export class ManageDashboardsCtrl {
   }
 
   getDashboards() {
-    return this.searchSrv.search(this.query).then((result) => {
+    return this.searchSrv.search(this.query).then(result => {
       return this.initDashboardList(result);
     });
   }
@@ -42,7 +53,10 @@ export class ManageDashboardsCtrl {
     this.canMove = false;
     this.canDelete = false;
     this.selectAllChecked = false;
-    this.hasFilters = this.query.query.length > 0 || this.query.tag.length > 0 || this.query.starred;
+    this.hasFilters =
+      this.query.query.length > 0 ||
+      this.query.tag.length > 0 ||
+      this.query.starred;
 
     if (!result) {
       this.sections = [];
@@ -87,7 +101,7 @@ export class ManageDashboardsCtrl {
         selectedDashboards.folders.push(section.slug);
       } else {
         const selected = _.filter(section.items, { checked: true });
-        selectedDashboards.dashboards.push(..._.map(selected, 'slug'));
+        selectedDashboards.dashboards.push(..._.map(selected, "slug"));
       }
     }
 
@@ -108,24 +122,30 @@ export class ManageDashboardsCtrl {
     const data = this.getFoldersAndDashboardsToDelete();
     const folderCount = data.folders.length;
     const dashCount = data.dashboards.length;
-    let text = 'Do you want to delete the ';
+    let text = "Do you want to delete the ";
     let text2;
 
     if (folderCount > 0 && dashCount > 0) {
-      text += `selected folder${folderCount === 1 ? '' : 's'} and dashboard${dashCount === 1 ? '' : 's'}?`;
-      text2 = `All dashboards of the selected folder${folderCount === 1 ? '' : 's'} will also be deleted`;
+      text += `selected folder${folderCount === 1 ? "" : "s"} and dashboard${
+        dashCount === 1 ? "" : "s"
+      }?`;
+      text2 = `All dashboards of the selected folder${
+        folderCount === 1 ? "" : "s"
+      } will also be deleted`;
     } else if (folderCount > 0) {
-      text += `selected folder${folderCount === 1 ? '' : 's'} and all its dashboards?`;
+      text += `selected folder${
+        folderCount === 1 ? "" : "s"
+      } and all its dashboards?`;
     } else {
-      text += `selected dashboard${dashCount === 1 ? '' : 's'}?`;
+      text += `selected dashboard${dashCount === 1 ? "" : "s"}?`;
     }
 
-    appEvents.emit('confirm-modal', {
-      title: 'Delete',
+    appEvents.emit("confirm-modal", {
+      title: "Delete",
       text: text,
       text2: text2,
-      icon: 'fa-trash',
-      yesText: 'Delete',
+      icon: "fa-trash",
+      yesText: "Delete",
       onConfirm: () => {
         const foldersAndDashboards = data.folders.concat(data.dashboards);
         this.deleteFoldersAndDashboards(foldersAndDashboards);
@@ -145,28 +165,36 @@ export class ManageDashboardsCtrl {
         let msg;
 
         if (folderCount > 0 && dashCount > 0) {
-          header = `Folder${folderCount === 1 ? '' : 's'} And Dashboard${dashCount === 1 ? '' : 's'} Deleted`;
-          msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} `;
-          msg += `and ${dashCount} dashboard${dashCount === 1 ? '' : 's'} has been deleted`;
+          header = `Folder${folderCount === 1 ? "" : "s"} And Dashboard${
+            dashCount === 1 ? "" : "s"
+          } Deleted`;
+          msg = `${folderCount} folder${folderCount === 1 ? "" : "s"} `;
+          msg += `and ${dashCount} dashboard${
+            dashCount === 1 ? "" : "s"
+          } has been deleted`;
         } else if (folderCount > 0) {
-          header = `Folder${folderCount === 1 ? '' : 's'} Deleted`;
+          header = `Folder${folderCount === 1 ? "" : "s"} Deleted`;
 
           if (folderCount === 1) {
             msg = `${folders[0].dashboard.title} has been deleted`;
           } else {
-            msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} has been deleted`;
+            msg = `${folderCount} folder${
+              folderCount === 1 ? "" : "s"
+            } has been deleted`;
           }
         } else if (dashCount > 0) {
-          header = `Dashboard${dashCount === 1 ? '' : 's'} Deleted`;
+          header = `Dashboard${dashCount === 1 ? "" : "s"} Deleted`;
 
           if (dashCount === 1) {
             msg = `${dashboards[0].dashboard.title} has been deleted`;
           } else {
-            msg = `${dashCount} dashboard${dashCount === 1 ? '' : 's'} has been deleted`;
+            msg = `${dashCount} dashboard${
+              dashCount === 1 ? "" : "s"
+            } has been deleted`;
           }
         }
 
-        appEvents.emit('alert-success', [header, msg]);
+        appEvents.emit("alert-success", [header, msg]);
       }
 
       this.getDashboards();
@@ -178,7 +206,7 @@ export class ManageDashboardsCtrl {
 
     for (const section of this.sections) {
       const selected = _.filter(section.items, { checked: true });
-      selectedDashboards.push(..._.map(selected, 'slug'));
+      selectedDashboards.push(..._.map(selected, "slug"));
     }
 
     return selectedDashboards;
@@ -187,19 +215,25 @@ export class ManageDashboardsCtrl {
   moveTo() {
     const selectedDashboards = this.getDashboardsToMove();
 
-    const template = '<move-to-folder-modal dismiss="dismiss()" ' +
+    const template =
+      '<move-to-folder-modal dismiss="dismiss()" ' +
       'dashboards="model.dashboards" after-save="model.afterSave()">' +
-      '</move-to-folder-modal>`';
-    appEvents.emit('show-modal', {
+      "</move-to-folder-modal>`";
+    appEvents.emit("show-modal", {
       templateHtml: template,
-      modalClass: 'modal--narrow',
-      model: { dashboards: selectedDashboards, afterSave: this.getDashboards.bind(this) }
+      modalClass: "modal--narrow",
+      model: {
+        dashboards: selectedDashboards,
+        afterSave: this.getDashboards.bind(this)
+      }
     });
   }
 
   getTags() {
-    return this.searchSrv.getDashboardTags().then((results) => {
-      this.tagFilterOptions = [{ term: 'Filter By Tag', disabled: true }].concat(results);
+    return this.searchSrv.getDashboardTags().then(results => {
+      this.tagFilterOptions = [
+        { term: "Filter By Tag", disabled: true }
+      ].concat(results);
       this.selectedTagFilter = this.tagFilterOptions[0];
     });
   }
@@ -237,7 +271,7 @@ export class ManageDashboardsCtrl {
   }
 
   onStarredFilterChange() {
-    this.query.starred = this.selectedStarredFilter.text === 'Yes';
+    this.query.starred = this.selectedStarredFilter.text === "Yes";
     this.selectedStarredFilter = this.starredFilterOptions[0];
     return this.getDashboards();
   }
@@ -248,7 +282,7 @@ export class ManageDashboardsCtrl {
         section.checked = this.selectAllChecked;
       }
 
-      section.items = _.map(section.items, (item) => {
+      section.items = _.map(section.items, item => {
         item.checked = this.selectAllChecked;
         return item;
       });
@@ -258,7 +292,7 @@ export class ManageDashboardsCtrl {
   }
 
   clearFilters() {
-    this.query.query = '';
+    this.query.query = "";
     this.query.tag = [];
     this.query.starred = false;
     this.getDashboards();
@@ -267,15 +301,16 @@ export class ManageDashboardsCtrl {
 
 export function manageDashboardsDirective() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/manage_dashboards/manage_dashboards.html',
+    restrict: "E",
+    templateUrl:
+      "public/app/core/components/manage_dashboards/manage_dashboards.html",
     controller: ManageDashboardsCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
-      folderId: '='
+      folderId: "="
     }
   };
 }
 
-coreModule.directive('manageDashboards', manageDashboardsDirective);
+coreModule.directive("manageDashboards", manageDashboardsDirective);

+ 13 - 15
public/app/core/components/navbar/navbar.ts

@@ -1,6 +1,6 @@
-import coreModule from '../../core_module';
-import {NavModel}  from '../../nav_model_srv';
-import appEvents from 'app/core/app_events';
+import coreModule from "../../core_module";
+import { NavModel } from "../../nav_model_srv";
+import appEvents from "app/core/app_events";
 
 export class NavbarCtrl {
   model: NavModel;
@@ -9,7 +9,7 @@ export class NavbarCtrl {
   constructor() {}
 
   showSearch() {
-    appEvents.emit('show-dash-search');
+    appEvents.emit("show-dash-search");
   }
 
   navItemClicked(navItem, evt) {
@@ -22,22 +22,21 @@ export class NavbarCtrl {
 
 export function navbarDirective() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/navbar/navbar.html',
+    restrict: "E",
+    templateUrl: "public/app/core/components/navbar/navbar.html",
     controller: NavbarCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
-      model: "=",
+      model: "="
     },
-    link: function(scope, elem) {
-    }
+    link: function(scope, elem) {}
   };
 }
 
 export function pageH1() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: `
     <h1 class="page-header__title">
       <i class="page-header__icon {{::model.header.icon}}" ng-if="::model.header.icon"></i>
@@ -46,11 +45,10 @@ export function pageH1() {
     </h1>
     `,
     scope: {
-      model: "=",
+      model: "="
     }
   };
 }
 
-
-coreModule.directive('pageH1', pageH1);
-coreModule.directive('navbar', navbarDirective);
+coreModule.directive("pageH1", pageH1);
+coreModule.directive("navbar", navbarDirective);

+ 11 - 9
public/app/core/components/org_switcher.ts

@@ -1,7 +1,7 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import coreModule from 'app/core/core_module';
-import {contextSrv} from 'app/core/services/context_srv';
+import coreModule from "app/core/core_module";
+import { contextSrv } from "app/core/services/context_srv";
 
 const template = `
 <div class="modal-body">
@@ -55,15 +55,17 @@ export class OrgSwitchCtrl {
   }
 
   getUserOrgs() {
-    this.backendSrv.get('/api/user/orgs').then(orgs => {
+    this.backendSrv.get("/api/user/orgs").then(orgs => {
       this.orgs = orgs;
     });
   }
 
   setUsingOrg(org) {
-    return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
+    return this.backendSrv.post("/api/user/using/" + org.orgId).then(() => {
       const re = /orgId=\d+/gi;
-      this.setWindowLocationHref(this.getWindowLocationHref().replace(re, 'orgId=' + org.orgId));
+      this.setWindowLocationHref(
+        this.getWindowLocationHref().replace(re, "orgId=" + org.orgId)
+      );
     });
   }
 
@@ -78,13 +80,13 @@ export class OrgSwitchCtrl {
 
 export function orgSwitcher() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     controller: OrgSwitchCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
-    scope: {dismiss: "&"},
+    controllerAs: "ctrl",
+    scope: { dismiss: "&" }
   };
 }
 
-coreModule.directive('orgSwitcher', orgSwitcher);
+coreModule.directive("orgSwitcher", orgSwitcher);

+ 19 - 20
public/app/core/components/query_part/query_part.ts

@@ -1,6 +1,6 @@
 ///<reference path="../../../headers/common.d.ts" />
 
-import _ from 'lodash';
+import _ from "lodash";
 
 export class QueryPartDef {
   type: string;
@@ -30,7 +30,7 @@ export class QueryPart {
     this.part = part;
     this.def = def;
     if (!this.def) {
-      throw {message: 'Could not find query part ' + part.type};
+      throw { message: "Could not find query part " + part.type };
     }
 
     part.params = part.params || _.clone(this.def.defaultParams);
@@ -42,25 +42,25 @@ export class QueryPart {
     return this.def.renderer(this, innerExpr);
   }
 
-  hasMultipleParamsInString (strValue, index) {
-    if (strValue.indexOf(',') === -1) {
+  hasMultipleParamsInString(strValue, index) {
+    if (strValue.indexOf(",") === -1) {
       return false;
     }
 
     return this.def.params[index + 1] && this.def.params[index + 1].optional;
   }
 
-  updateParam (strValue, index) {
+  updateParam(strValue, index) {
     // handle optional parameters
     // if string contains ',' and next param is optional, split and update both
     if (this.hasMultipleParamsInString(strValue, index)) {
-      _.each(strValue.split(','), (partVal, idx) => {
+      _.each(strValue.split(","), (partVal, idx) => {
         this.updateParam(partVal.trim(), idx);
       });
       return;
     }
 
-    if (strValue === '' && this.def.params[index].optional) {
+    if (strValue === "" && this.def.params[index].optional) {
       this.params.splice(index, 1);
     } else {
       this.params[index] = strValue;
@@ -72,29 +72,29 @@ export class QueryPart {
 
   updateText() {
     if (this.params.length === 0) {
-      this.text = this.def.type + '()';
+      this.text = this.def.type + "()";
       return;
     }
 
-    var text = this.def.type + '(';
-    text += this.params.join(', ');
-    text += ')';
+    var text = this.def.type + "(";
+    text += this.params.join(", ");
+    text += ")";
     this.text = text;
   }
 }
 
 export function functionRenderer(part, innerExpr) {
-  var str = part.def.type + '(';
+  var str = part.def.type + "(";
   var parameters = _.map(part.params, (value, index) => {
     var paramType = part.def.params[index];
-    if (paramType.type === 'time') {
-      if (value === 'auto') {
-        value = '$__interval';
+    if (paramType.type === "time") {
+      if (value === "auto") {
+        value = "$__interval";
       }
     }
-    if (paramType.quote === 'single') {
+    if (paramType.quote === "single") {
       return "'" + value + "'";
-    } else if (paramType.quote === 'double') {
+    } else if (paramType.quote === "double") {
       return '"' + value + '"';
     }
 
@@ -104,12 +104,11 @@ export function functionRenderer(part, innerExpr) {
   if (innerExpr) {
     parameters.unshift(innerExpr);
   }
-  return str + parameters.join(', ') + ')';
+  return str + parameters.join(", ") + ")";
 }
 
-
 export function suffixRenderer(part, innerExpr) {
-  return innerExpr + ' ' + part.params[0];
+  return innerExpr + " " + part.params[0];
 }
 
 export function identityRenderer(part, innerExpr) {

+ 47 - 35
public/app/core/components/query_part/query_part_editor.ts

@@ -1,8 +1,8 @@
 ///<reference path="../../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import $ from 'jquery';
-import coreModule from 'app/core/core_module';
+import _ from "lodash";
+import $ from "jquery";
+import coreModule from "app/core/core_module";
 
 var template = `
 <div class="dropdown cascade-open">
@@ -15,22 +15,22 @@ var template = `
 </ul>
 `;
 
-  /** @ngInject */
+/** @ngInject */
 export function queryPartEditorDirective($compile, templateSrv) {
-
-  var paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
+  var paramTemplate =
+    '<input type="text" class="hide input-mini tight-form-func-param"></input>';
 
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     scope: {
       part: "=",
-      handleEvent: "&",
+      handleEvent: "&"
     },
     link: function postLink($scope, elem) {
       var part = $scope.part;
       var partDef = part.def;
-      var $paramsContainer = elem.find('.query-part-parameters');
+      var $paramsContainer = elem.find(".query-part-parameters");
 
       $scope.partActions = [];
 
@@ -40,16 +40,16 @@ export function queryPartEditorDirective($compile, templateSrv) {
         var $input = $link.next();
 
         $input.val(part.params[paramIndex]);
-        $input.css('width', ($link.width() + 16) + 'px');
+        $input.css("width", $link.width() + 16 + "px");
 
         $link.hide();
         $input.show();
         $input.focus();
         $input.select();
 
-        var typeahead = $input.data('typeahead');
+        var typeahead = $input.data("typeahead");
         if (typeahead) {
-          $input.val('');
+          $input.val("");
           typeahead.lookup();
         }
       }
@@ -60,12 +60,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
         var $link = $input.prev();
         var newValue = $input.val();
 
-        if (newValue !== '' || part.def.params[paramIndex].optional) {
+        if (newValue !== "" || part.def.params[paramIndex].optional) {
           $link.html(templateSrv.highlightVariablesAsHtml(newValue));
 
           part.updateParam($input.val(), paramIndex);
           $scope.$apply(() => {
-            $scope.handleEvent({$event: {name: 'part-param-changed'}});
+            $scope.handleEvent({ $event: { name: "part-param-changed" } });
           });
         }
 
@@ -82,7 +82,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
 
       function inputKeyDown() {
         /*jshint validthis:true */
-        this.style.width = (3 + this.value.length) * 8 + 'px';
+        this.style.width = (3 + this.value.length) * 8 + "px";
       }
 
       function addTypeahead($input, param, paramIndex) {
@@ -90,30 +90,36 @@ export function queryPartEditorDirective($compile, templateSrv) {
           return;
         }
 
-        var typeaheadSource = function (query, callback) {
+        var typeaheadSource = function(query, callback) {
           if (param.options) {
             var options = param.options;
-            if (param.type === 'int') {
-              options = _.map(options, function(val) { return val.toString(); });
+            if (param.type === "int") {
+              options = _.map(options, function(val) {
+                return val.toString();
+              });
             }
             return options;
           }
 
           $scope.$apply(function() {
-            $scope.handleEvent({$event: {name: 'get-param-options'}}).then(function(result) {
-              var dynamicOptions = _.map(result, function(op) { return op.value; });
-              callback(dynamicOptions);
-            });
+            $scope
+              .handleEvent({ $event: { name: "get-param-options" } })
+              .then(function(result) {
+                var dynamicOptions = _.map(result, function(op) {
+                  return op.value;
+                });
+                callback(dynamicOptions);
+              });
           });
         };
 
-        $input.attr('data-provide', 'typeahead');
+        $input.attr("data-provide", "typeahead");
 
         $input.typeahead({
           source: typeaheadSource,
           minLength: 0,
           items: 1000,
-          updater: function (value) {
+          updater: function(value) {
             setTimeout(function() {
               inputBlur.call($input[0], paramIndex);
             }, 0);
@@ -121,22 +127,24 @@ export function queryPartEditorDirective($compile, templateSrv) {
           }
         });
 
-        var typeahead = $input.data('typeahead');
-        typeahead.lookup = function () {
-          this.query = this.$element.val() || '';
+        var typeahead = $input.data("typeahead");
+        typeahead.lookup = function() {
+          this.query = this.$element.val() || "";
           var items = this.source(this.query, $.proxy(this.process, this));
           return items ? this.process(items) : items;
         };
       }
 
       $scope.showActionsMenu = function() {
-        $scope.handleEvent({$event: {name: 'get-part-actions'}}).then(res => {
-          $scope.partActions = res;
-        });
+        $scope
+          .handleEvent({ $event: { name: "get-part-actions" } })
+          .then(res => {
+            $scope.partActions = res;
+          });
       };
 
       $scope.triggerPartAction = function(action) {
-        $scope.handleEvent({$event: {name: 'action', action: action}});
+        $scope.handleEvent({ $event: { name: "action", action: action } });
       };
 
       function addElementsAndCompile() {
@@ -146,11 +154,15 @@ export function queryPartEditorDirective($compile, templateSrv) {
           }
 
           if (index > 0) {
-            $('<span>, </span>').appendTo($paramsContainer);
+            $("<span>, </span>").appendTo($paramsContainer);
           }
 
-          var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
-          var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
+          var paramValue = templateSrv.highlightVariablesAsHtml(
+            part.params[index]
+          );
+          var $paramLink = $(
+            '<a class="graphite-func-param-link pointer">' + paramValue + "</a>"
+          );
           var $input = $(paramTemplate);
 
           $paramLink.appendTo($paramsContainer);
@@ -175,4 +187,4 @@ export function queryPartEditorDirective($compile, templateSrv) {
   };
 }
 
-coreModule.directive('queryPartEditor', queryPartEditorDirective);
+coreModule.directive("queryPartEditor", queryPartEditorDirective);

+ 7 - 9
public/app/core/components/scroll/scroll.ts

@@ -1,27 +1,25 @@
-import PerfectScrollbar from 'perfect-scrollbar';
-import coreModule from 'app/core/core_module';
+import PerfectScrollbar from "perfect-scrollbar";
+import coreModule from "app/core/core_module";
 
 export function geminiScrollbar() {
   return {
-    restrict: 'A',
+    restrict: "A",
     link: function(scope, elem, attrs) {
-
       let scrollbar = new PerfectScrollbar(elem[0]);
 
-      scope.$on('$routeChangeSuccess', () => {
+      scope.$on("$routeChangeSuccess", () => {
         elem[0].scrollTop = 0;
       });
 
-      scope.$on('$routeUpdate', () => {
+      scope.$on("$routeUpdate", () => {
         elem[0].scrollTop = 0;
       });
 
-      scope.$on('$destroy', () => {
+      scope.$on("$destroy", () => {
         scrollbar.destroy();
       });
-
     }
   };
 }
 
-coreModule.directive('grafanaScrollbar', geminiScrollbar);
+coreModule.directive("grafanaScrollbar", geminiScrollbar);

+ 48 - 28
public/app/core/components/search/search.ts

@@ -1,7 +1,7 @@
-import _ from 'lodash';
-import coreModule from '../../core_module';
-import { SearchSrv } from 'app/core/services/search_srv';
-import appEvents from 'app/core/app_events';
+import _ from "lodash";
+import coreModule from "../../core_module";
+import { SearchSrv } from "app/core/services/search_srv";
+import appEvents from "app/core/app_events";
 
 export class SearchCtrl {
   isOpen: boolean;
@@ -17,9 +17,14 @@ export class SearchCtrl {
   initialFolderFilterTitle: string;
 
   /** @ngInject */
-  constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv) {
-    appEvents.on('show-dash-search', this.openSearch.bind(this), $scope);
-    appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope);
+  constructor(
+    $scope,
+    private $location,
+    private $timeout,
+    private searchSrv: SearchSrv
+  ) {
+    appEvents.on("show-dash-search", this.openSearch.bind(this), $scope);
+    appEvents.on("hide-dash-search", this.closeSearch.bind(this), $scope);
 
     this.initialFolderFilterTitle = "All";
   }
@@ -38,7 +43,7 @@ export class SearchCtrl {
     this.giveSearchFocus = 0;
     this.selectedIndex = -1;
     this.results = [];
-    this.query = { query: '', tag: [], starred: false };
+    this.query = { query: "", tag: [], starred: false };
     this.currentSearchId = 0;
     this.ignoreClose = true;
     this.isLoading = true;
@@ -70,7 +75,9 @@ export class SearchCtrl {
 
       if (currentItem) {
         if (currentItem.dashboardIndex !== undefined) {
-          const selectedDash = this.results[currentItem.folderIndex].items[currentItem.dashboardIndex];
+          const selectedDash = this.results[currentItem.folderIndex].items[
+            currentItem.dashboardIndex
+          ];
 
           if (selectedDash) {
             this.$location.search({});
@@ -98,7 +105,9 @@ export class SearchCtrl {
 
     if (currentItem) {
       if (currentItem.dashboardIndex !== undefined) {
-        this.results[currentItem.folderIndex].items[currentItem.dashboardIndex].selected = false;
+        this.results[currentItem.folderIndex].items[
+          currentItem.dashboardIndex
+        ].selected = false;
       } else {
         this.results[currentItem.folderIndex].selected = false;
       }
@@ -111,10 +120,13 @@ export class SearchCtrl {
 
     const max = flattenedResult.length;
     let newIndex = this.selectedIndex + direction;
-    this.selectedIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
+    this.selectedIndex = (newIndex %= max) < 0 ? newIndex + max : newIndex;
     const selectedItem = flattenedResult[this.selectedIndex];
 
-    if (selectedItem.dashboardIndex === undefined && this.results[selectedItem.folderIndex].id === 0) {
+    if (
+      selectedItem.dashboardIndex === undefined &&
+      this.results[selectedItem.folderIndex].id === 0
+    ) {
       this.moveSelection(direction);
       return;
     }
@@ -125,7 +137,9 @@ export class SearchCtrl {
         return;
       }
 
-      this.results[selectedItem.folderIndex].items[selectedItem.dashboardIndex].selected = true;
+      this.results[selectedItem.folderIndex].items[
+        selectedItem.dashboardIndex
+      ].selected = true;
       return;
     }
 
@@ -142,7 +156,9 @@ export class SearchCtrl {
     var localSearchId = this.currentSearchId;
 
     return this.searchSrv.search(this.query).then(results => {
-      if (localSearchId < this.currentSearchId) { return; }
+      if (localSearchId < this.currentSearchId) {
+        return;
+      }
       this.results = results || [];
       this.isLoading = false;
       this.moveSelection(1);
@@ -151,7 +167,9 @@ export class SearchCtrl {
 
   queryHasNoFilters() {
     var query = this.query;
-    return query.query === '' && query.starred === false && query.tag.length === 0;
+    return (
+      query.query === "" && query.starred === false && query.tag.length === 0
+    );
   }
 
   filterByTag(tag) {
@@ -171,7 +189,7 @@ export class SearchCtrl {
   }
 
   getTags() {
-    return this.searchSrv.getDashboardTags().then((results) => {
+    return this.searchSrv.getDashboardTags().then(results => {
       this.results = results;
       this.giveSearchFocus = this.giveSearchFocus + 1;
     });
@@ -196,7 +214,7 @@ export class SearchCtrl {
   private getFlattenedResultForNavigation() {
     let folderIndex = 0;
 
-    return _.flatMap(this.results, (s) => {
+    return _.flatMap(this.results, s => {
       let result = [];
 
       result.push({
@@ -205,12 +223,14 @@ export class SearchCtrl {
 
       let dashboardIndex = 0;
 
-      result = result.concat(_.map(s.items || [], (i) => {
-        return {
-          folderIndex: folderIndex,
-          dashboardIndex: dashboardIndex++
-        };
-      }));
+      result = result.concat(
+        _.map(s.items || [], i => {
+          return {
+            folderIndex: folderIndex,
+            dashboardIndex: dashboardIndex++
+          };
+        })
+      );
 
       folderIndex++;
       return result;
@@ -220,13 +240,13 @@ export class SearchCtrl {
 
 export function searchDirective() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/search/search.html',
+    restrict: "E",
+    templateUrl: "public/app/core/components/search/search.html",
     controller: SearchCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
-    scope: {},
+    controllerAs: "ctrl",
+    scope: {}
   };
 }
 
-coreModule.directive('dashboardSearch', searchDirective);
+coreModule.directive("dashboardSearch", searchDirective);

+ 15 - 15
public/app/core/components/search/search_results.ts

@@ -1,6 +1,6 @@
-import _ from 'lodash';
-import coreModule from '../../core_module';
-import appEvents from 'app/core/app_events';
+import _ from "lodash";
+import coreModule from "../../core_module";
+import appEvents from "app/core/app_events";
 
 export class SearchResultsCtrl {
   results: any;
@@ -64,13 +64,13 @@ export class SearchResultsCtrl {
 
   onItemClick(item) {
     if (this.$location.path().indexOf(item.url) > -1) {
-      appEvents.emit('hide-dash-search');
+      appEvents.emit("hide-dash-search");
     }
   }
 
   selectTag(tag, evt) {
     if (this.onTagSelected) {
-      this.onTagSelected({$tag: tag});
+      this.onTagSelected({ $tag: tag });
     }
 
     if (evt) {
@@ -82,19 +82,19 @@ export class SearchResultsCtrl {
 
 export function searchResultsDirective() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/search/search_results.html',
+    restrict: "E",
+    templateUrl: "public/app/core/components/search/search_results.html",
     controller: SearchResultsCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
-      editable: '@',
-      results: '=',
-      onSelectionChanged: '&',
-      onTagSelected: '&',
-      onFolderExpanding: '&'
-    },
+      editable: "@",
+      results: "=",
+      onSelectionChanged: "&",
+      onTagSelected: "&",
+      onFolderExpanding: "&"
+    }
   };
 }
 
-coreModule.directive('dashboardSearchResults', searchResultsDirective);
+coreModule.directive("dashboardSearchResults", searchResultsDirective);

+ 41 - 25
public/app/core/components/sidemenu/sidemenu.ts

@@ -1,8 +1,8 @@
-import _ from 'lodash';
-import config from 'app/core/config';
-import $ from 'jquery';
-import coreModule from '../../core_module';
-import appEvents from 'app/core/app_events';
+import _ from "lodash";
+import config from "app/core/config";
+import $ from "jquery";
+import coreModule from "../../core_module";
+import appEvents from "app/core/app_events";
 
 export class SideMenuCtrl {
   user: any;
@@ -13,47 +13,63 @@ export class SideMenuCtrl {
   isOpenMobile: boolean;
 
   /** @ngInject */
-  constructor(private $scope, private $rootScope, private $location, private contextSrv, private $timeout) {
+  constructor(
+    private $scope,
+    private $rootScope,
+    private $location,
+    private contextSrv,
+    private $timeout
+  ) {
     this.isSignedIn = contextSrv.isSignedIn;
     this.user = contextSrv.user;
-    this.mainLinks = _.filter(config.bootData.navTree, item => !item.hideFromMenu);
-    this.bottomNav = _.filter(config.bootData.navTree, item => item.hideFromMenu);
-    this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
+    this.mainLinks = _.filter(
+      config.bootData.navTree,
+      item => !item.hideFromMenu
+    );
+    this.bottomNav = _.filter(
+      config.bootData.navTree,
+      item => item.hideFromMenu
+    );
+    this.loginUrl =
+      "login?redirect=" + encodeURIComponent(this.$location.path());
 
     if (contextSrv.user.orgCount > 1) {
-      let profileNode = _.find(this.bottomNav, {id: 'profile'});
+      let profileNode = _.find(this.bottomNav, { id: "profile" });
       if (profileNode) {
         profileNode.showOrgSwitcher = true;
       }
     }
 
-    this.$scope.$on('$routeChangeSuccess', () => {
-      this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
+    this.$scope.$on("$routeChangeSuccess", () => {
+      this.loginUrl =
+        "login?redirect=" + encodeURIComponent(this.$location.path());
     });
   }
 
   toggleSideMenu() {
     this.contextSrv.toggleSideMenu();
-    appEvents.emit('toggle-sidemenu');
+    appEvents.emit("toggle-sidemenu");
 
     this.$timeout(() => {
-      this.$rootScope.$broadcast('render');
+      this.$rootScope.$broadcast("render");
     });
   }
 
   toggleSideMenuSmallBreakpoint() {
-    appEvents.emit('toggle-sidemenu-mobile');
+    appEvents.emit("toggle-sidemenu-mobile");
   }
 
   switchOrg() {
-    this.$rootScope.appEvent('show-modal', {
-      templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>',
+    this.$rootScope.appEvent("show-modal", {
+      templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>'
     });
   }
 
   itemClicked(item, evt) {
-    if (item.url === '/shortcuts') {
-      appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
+    if (item.url === "/shortcuts") {
+      appEvents.emit("show-modal", {
+        templateHtml: "<help-modal></help-modal>"
+      });
       evt.preventDefault();
     }
   }
@@ -61,16 +77,16 @@ export class SideMenuCtrl {
 
 export function sideMenuDirective() {
   return {
-    restrict: 'E',
-    templateUrl: 'public/app/core/components/sidemenu/sidemenu.html',
+    restrict: "E",
+    templateUrl: "public/app/core/components/sidemenu/sidemenu.html",
     controller: SideMenuCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {},
     link: function(scope, elem) {
       // hack to hide dropdown menu
-      elem.on('click.dropdown', '.dropdown-menu a', function(evt) {
-        var menu = $(evt.target).parents('.dropdown-menu');
+      elem.on("click.dropdown", ".dropdown-menu a", function(evt) {
+        var menu = $(evt.target).parents(".dropdown-menu");
         var parent = menu.parent();
         menu.detach();
 
@@ -82,4 +98,4 @@ export function sideMenuDirective() {
   };
 }
 
-coreModule.directive('sidemenu', sideMenuDirective);
+coreModule.directive("sidemenu", sideMenuDirective);

+ 6 - 7
public/app/core/components/switch.ts

@@ -1,6 +1,6 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import coreModule from 'app/core/core_module';
+import coreModule from "app/core/core_module";
 
 var template = `
 <label for="check-{{ctrl.id}}" class="gf-form-label {{ctrl.labelClass}} pointer" ng-show="ctrl.label">
@@ -33,14 +33,13 @@ export class SwitchCtrl {
       return this.onChange();
     });
   }
-
 }
 
 export function switchDirective() {
   return {
-    restrict: 'E',
+    restrict: "E",
     controller: SwitchCtrl,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     bindToController: true,
     scope: {
       checked: "=",
@@ -48,10 +47,10 @@ export function switchDirective() {
       labelClass: "@",
       tooltip: "@",
       switchClass: "@",
-      onChange: "&",
+      onChange: "&"
     },
-    template: template,
+    template: template
   };
 }
 
-coreModule.directive('gfFormSwitch', switchDirective);
+coreModule.directive("gfFormSwitch", switchDirective);

+ 21 - 14
public/app/core/components/team_picker.ts

@@ -1,5 +1,5 @@
-import coreModule from 'app/core/core_module';
-import _ from 'lodash';
+import coreModule from "app/core/core_module";
+import _ from "lodash";
 
 const template = `
 <div class="dropdown">
@@ -17,36 +17,43 @@ export class TeamPickerCtrl {
 
   /** @ngInject */
   constructor(private backendSrv) {
-    this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {'leading': true, 'trailing': false});
+    this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {
+      leading: true,
+      trailing: false
+    });
     this.reset();
   }
 
   reset() {
-    this.group = {text: 'Choose', value: null};
+    this.group = { text: "Choose", value: null };
   }
 
   searchGroups(query: string) {
-    return Promise.resolve(this.backendSrv.get('/api/teams/search?perpage=10&page=1&query=' + query).then(result => {
-      return _.map(result.teams, ug => {
-        return {text: ug.name, value: ug};
-      });
-    }));
+    return Promise.resolve(
+      this.backendSrv
+        .get("/api/teams/search?perpage=10&page=1&query=" + query)
+        .then(result => {
+          return _.map(result.teams, ug => {
+            return { text: ug.name, value: ug };
+          });
+        })
+    );
   }
 
   onChange(option) {
-    this.teamPicked({$group: option.value});
+    this.teamPicked({ $group: option.value });
   }
 }
 
 export function teamPicker() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     controller: TeamPickerCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
-      teamPicked: '&',
+      teamPicked: "&"
     },
     link: function(scope, elem, attrs, ctrl) {
       scope.$on("team-picker-reset", () => {
@@ -56,4 +63,4 @@ export function teamPicker() {
   };
 }
 
-coreModule.directive('teamPicker', teamPicker);
+coreModule.directive("teamPicker", teamPicker);

+ 21 - 14
public/app/core/components/user_picker.ts

@@ -1,5 +1,5 @@
-import coreModule from 'app/core/core_module';
-import _ from 'lodash';
+import coreModule from "app/core/core_module";
+import _ from "lodash";
 
 const template = `
 <div class="dropdown">
@@ -18,23 +18,30 @@ export class UserPickerCtrl {
   /** @ngInject */
   constructor(private backendSrv) {
     this.reset();
-    this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {'leading': true, 'trailing': false});
+    this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {
+      leading: true,
+      trailing: false
+    });
   }
 
   searchUsers(query: string) {
-    return Promise.resolve(this.backendSrv.get('/api/users/search?perpage=10&page=1&query=' + query).then(result => {
-      return _.map(result.users, user => {
-        return {text: user.login + ' -  ' + user.email, value: user};
-      });
-    }));
+    return Promise.resolve(
+      this.backendSrv
+        .get("/api/users/search?perpage=10&page=1&query=" + query)
+        .then(result => {
+          return _.map(result.users, user => {
+            return { text: user.login + " -  " + user.email, value: user };
+          });
+        })
+    );
   }
 
   onChange(option) {
-    this.userPicked({$user: option.value});
+    this.userPicked({ $user: option.value });
   }
 
   reset() {
-    this.user = {text: 'Choose', value: null};
+    this.user = { text: "Choose", value: null };
   }
 }
 
@@ -47,13 +54,13 @@ export interface User {
 
 export function userPicker() {
   return {
-    restrict: 'E',
+    restrict: "E",
     template: template,
     controller: UserPickerCtrl,
     bindToController: true,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     scope: {
-      userPicked: '&',
+      userPicked: "&"
     },
     link: function(scope, elem, attrs, ctrl) {
       scope.$on("user-picker-reset", () => {
@@ -63,4 +70,4 @@ export function userPicker() {
   };
 }
 
-coreModule.directive('userPicker', userPicker);
+coreModule.directive("userPicker", userPicker);

+ 33 - 33
public/app/core/config.ts

@@ -1,39 +1,39 @@
-import _ from 'lodash';
+import _ from "lodash";
 
 class Settings {
-    datasources: any;
-    panels: any;
-    appSubUrl: string;
-    window_title_prefix: string;
-    buildInfo: any;
-    new_panel_title: string;
-    bootData: any;
-    externalUserMngLinkUrl: string;
-    externalUserMngLinkName: string;
-    externalUserMngInfo: string;
-    allowOrgCreate: boolean;
-    disableLoginForm: boolean;
-    defaultDatasource: string;
-    alertingEnabled: boolean;
-    authProxyEnabled: boolean;
-    ldapEnabled: boolean;
-    oauth: any;
-    disableUserSignUp: boolean;
-    loginHint: any;
-    loginError: any;
+  datasources: any;
+  panels: any;
+  appSubUrl: string;
+  window_title_prefix: string;
+  buildInfo: any;
+  new_panel_title: string;
+  bootData: any;
+  externalUserMngLinkUrl: string;
+  externalUserMngLinkName: string;
+  externalUserMngInfo: string;
+  allowOrgCreate: boolean;
+  disableLoginForm: boolean;
+  defaultDatasource: string;
+  alertingEnabled: boolean;
+  authProxyEnabled: boolean;
+  ldapEnabled: boolean;
+  oauth: any;
+  disableUserSignUp: boolean;
+  loginHint: any;
+  loginError: any;
 
-    constructor(options) {
-        var defaults = {
-            datasources: {},
-            window_title_prefix: 'Grafana - ',
-            panels: {},
-            new_panel_title: 'Panel Title',
-            playlist_timespan: "1m",
-            unsaved_changes_warning: true,
-            appSubUrl: ""
-        };
-         _.extend(this, defaults, options);
-    }
+  constructor(options) {
+    var defaults = {
+      datasources: {},
+      window_title_prefix: "Grafana - ",
+      panels: {},
+      new_panel_title: "Panel Title",
+      playlist_timespan: "1m",
+      unsaved_changes_warning: true,
+      appSubUrl: ""
+    };
+    _.extend(this, defaults, options);
+  }
 }
 
 var bootData = (<any>window).grafanaBootData || { settings: {} };

+ 1 - 2
public/app/core/constants.ts

@@ -1,8 +1,7 @@
-
 export const GRID_CELL_HEIGHT = 30;
 export const GRID_CELL_VMARGIN = 10;
 export const GRID_COLUMN_COUNT = 24;
-export const REPEAT_DIR_VERTICAL = 'v';
+export const REPEAT_DIR_VERTICAL = "v";
 
 export const DEFAULT_PANEL_SPAN = 4;
 export const DEFAULT_ROW_HEIGHT = 250;

+ 6 - 7
public/app/core/controllers/error_ctrl.ts

@@ -1,24 +1,23 @@
-import config from 'app/core/config';
-import coreModule from '../core_module';
-import appEvents from 'app/core/app_events';
+import config from "app/core/config";
+import coreModule from "../core_module";
+import appEvents from "app/core/app_events";
 
 export class ErrorCtrl {
-
   /** @ngInject */
   constructor($scope, contextSrv, navModelSrv) {
     $scope.navModel = navModelSrv.getNotFoundNav();
     $scope.appSubUrl = config.appSubUrl;
 
     if (!contextSrv.isSignedIn) {
-      appEvents.emit('toggle-sidemenu-hidden');
+      appEvents.emit("toggle-sidemenu-hidden");
     }
 
     $scope.$on("destroy", () => {
       if (!contextSrv.isSignedIn) {
-        appEvents.emit('toggle-sidemenu-hidden');
+        appEvents.emit("toggle-sidemenu-hidden");
       }
     });
   }
 }
 
-coreModule.controller('ErrorCtrl', ErrorCtrl);
+coreModule.controller("ErrorCtrl", ErrorCtrl);

+ 25 - 15
public/app/core/controllers/inspect_ctrl.ts

@@ -1,18 +1,17 @@
-import angular from 'angular';
-import _ from 'lodash';
-import $ from 'jquery';
-import coreModule from '../core_module';
+import angular from "angular";
+import _ from "lodash";
+import $ from "jquery";
+import coreModule from "../core_module";
 
 export class InspectCtrl {
-
   /** @ngInject */
   constructor($scope, $sanitize) {
     var model = $scope.inspector;
 
-    $scope.init = function () {
+    $scope.init = function() {
       $scope.editor = { index: 0 };
 
-      if (!model.error)  {
+      if (!model.error) {
         return;
       }
 
@@ -29,8 +28,11 @@ export class InspectCtrl {
       }
 
       if (model.error.config && model.error.config.params) {
-        $scope.request_parameters = _.map(model.error.config.params, function(value, key) {
-          return { key: key, value: value};
+        $scope.request_parameters = _.map(model.error.config.params, function(
+          value,
+          key
+        ) {
+          return { key: key, value: value };
         });
       }
 
@@ -44,10 +46,15 @@ export class InspectCtrl {
         $scope.editor.index = 2;
 
         if (_.isString(model.error.config.data)) {
-          $scope.request_parameters = this.getParametersFromQueryString(model.error.config.data);
-        } else  {
-          $scope.request_parameters = _.map(model.error.config.data, function(value, key) {
-            return {key: key, value: angular.toJson(value, true)};
+          $scope.request_parameters = this.getParametersFromQueryString(
+            model.error.config.data
+          );
+        } else {
+          $scope.request_parameters = _.map(model.error.config.data, function(
+            value,
+            key
+          ) {
+            return { key: key, value: angular.toJson(value, true) };
           });
         }
       }
@@ -59,11 +66,14 @@ export class InspectCtrl {
     for (var i = 0; i < parameters.length; i++) {
       var keyValue = parameters[i].split("=");
       if (keyValue[1].length > 0) {
-        result.push({ key: keyValue[0], value: (<any>window).unescape(keyValue[1]) });
+        result.push({
+          key: keyValue[0],
+          value: (<any>window).unescape(keyValue[1])
+        });
       }
     }
     return result;
   }
 }
 
-coreModule.controller('InspectCtrl', InspectCtrl);
+coreModule.controller("InspectCtrl", InspectCtrl);

+ 22 - 22
public/app/core/controllers/invited_ctrl.ts

@@ -1,8 +1,7 @@
-import coreModule from '../core_module';
-import config from 'app/core/config';
+import coreModule from "../core_module";
+import config from "app/core/config";
 
 export class InvitedCtrl {
-
   /** @ngInject */
   constructor($scope, $routeParams, contextSrv, backendSrv) {
     contextSrv.sidemenu = false;
@@ -10,25 +9,24 @@ export class InvitedCtrl {
 
     $scope.navModel = {
       main: {
-        icon: 'gicon gicon-branding',
-        subTitle: 'Register your Grafana account',
-        breadcrumbs: [
-          { title: 'Login', url: '/login' },
-          { title: 'Invite' },
-        ]
+        icon: "gicon gicon-branding",
+        subTitle: "Register your Grafana account",
+        breadcrumbs: [{ title: "Login", url: "/login" }, { title: "Invite" }]
       }
     };
 
     $scope.init = function() {
-      backendSrv.get('/api/user/invite/' + $routeParams.code).then(function(invite) {
-        $scope.formModel.name = invite.name;
-        $scope.formModel.email = invite.email;
-        $scope.formModel.username = invite.email;
-        $scope.formModel.inviteCode =  $routeParams.code;
-
-        $scope.greeting = invite.name || invite.email || invite.username;
-        $scope.invitedBy = invite.invitedBy;
-      });
+      backendSrv
+        .get("/api/user/invite/" + $routeParams.code)
+        .then(function(invite) {
+          $scope.formModel.name = invite.name;
+          $scope.formModel.email = invite.email;
+          $scope.formModel.username = invite.email;
+          $scope.formModel.inviteCode = $routeParams.code;
+
+          $scope.greeting = invite.name || invite.email || invite.username;
+          $scope.invitedBy = invite.invitedBy;
+        });
     };
 
     $scope.submit = function() {
@@ -36,13 +34,15 @@ export class InvitedCtrl {
         return;
       }
 
-      backendSrv.post('/api/user/invite/complete', $scope.formModel).then(function() {
-        window.location.href = config.appSubUrl + '/';
-      });
+      backendSrv
+        .post("/api/user/invite/complete", $scope.formModel)
+        .then(function() {
+          window.location.href = config.appSubUrl + "/";
+        });
     };
 
     $scope.init();
   }
 }
 
-coreModule.controller('InvitedCtrl', InvitedCtrl);
+coreModule.controller("InvitedCtrl", InvitedCtrl);

+ 6 - 6
public/app/core/controllers/json_editor_ctrl.ts

@@ -1,18 +1,18 @@
-import angular from 'angular';
-import coreModule from '../core_module';
+import angular from "angular";
+import coreModule from "../core_module";
 
 export class JsonEditorCtrl {
-
   /** @ngInject */
   constructor($scope) {
     $scope.json = angular.toJson($scope.object, true);
-    $scope.canUpdate = $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor;
+    $scope.canUpdate =
+      $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor;
 
-    $scope.update = function () {
+    $scope.update = function() {
       var newObject = angular.fromJson($scope.json);
       $scope.updateHandler(newObject, $scope.object);
     };
   }
 }
 
-coreModule.controller('JsonEditorCtrl', JsonEditorCtrl);
+coreModule.controller("JsonEditorCtrl", JsonEditorCtrl);

+ 23 - 22
public/app/core/controllers/login_ctrl.ts

@@ -1,15 +1,14 @@
-import _ from 'lodash';
-import coreModule from '../core_module';
-import config from 'app/core/config';
+import _ from "lodash";
+import coreModule from "../core_module";
+import config from "app/core/config";
 
 export class LoginCtrl {
-
   /** @ngInject */
   constructor($scope, backendSrv, contextSrv, $location) {
     $scope.formModel = {
-      user: '',
-      email: '',
-      password: '',
+      user: "",
+      email: "",
+      password: ""
     };
 
     contextSrv.sidemenu = false;
@@ -19,16 +18,16 @@ export class LoginCtrl {
 
     $scope.disableLoginForm = config.disableLoginForm;
     $scope.disableUserSignUp = config.disableUserSignUp;
-    $scope.loginHint     = config.loginHint;
+    $scope.loginHint = config.loginHint;
 
     $scope.loginMode = true;
-    $scope.submitBtnText = 'Log in';
+    $scope.submitBtnText = "Log in";
 
     $scope.init = function() {
       $scope.$watch("loginMode", $scope.loginModeChanged);
 
       if (config.loginError) {
-        $scope.appEvent('alert-warning', ['Login Failed', config.loginError]);
+        $scope.appEvent("alert-warning", ["Login Failed", config.loginError]);
       }
     };
 
@@ -41,7 +40,7 @@ export class LoginCtrl {
     };
 
     $scope.loginModeChanged = function(newValue) {
-      $scope.submitBtnText = newValue ? 'Log in' : 'Sign up';
+      $scope.submitBtnText = newValue ? "Log in" : "Sign up";
     };
 
     $scope.signUp = function() {
@@ -49,13 +48,15 @@ export class LoginCtrl {
         return;
       }
 
-      backendSrv.post('/api/user/signup', $scope.formModel).then(function(result) {
-        if (result.status === 'SignUpCreated') {
-          $location.path('/signup').search({email: $scope.formModel.email});
-        } else {
-          window.location.href = config.appSubUrl + '/';
-        }
-      });
+      backendSrv
+        .post("/api/user/signup", $scope.formModel)
+        .then(function(result) {
+          if (result.status === "SignUpCreated") {
+            $location.path("/signup").search({ email: $scope.formModel.email });
+          } else {
+            window.location.href = config.appSubUrl + "/";
+          }
+        });
     };
 
     $scope.login = function() {
@@ -65,15 +66,15 @@ export class LoginCtrl {
         return;
       }
 
-      backendSrv.post('/login', $scope.formModel).then(function(result) {
+      backendSrv.post("/login", $scope.formModel).then(function(result) {
         var params = $location.search();
 
-        if (params.redirect && params.redirect[0] === '/') {
+        if (params.redirect && params.redirect[0] === "/") {
           window.location.href = config.appSubUrl + params.redirect;
         } else if (result.redirectUrl) {
           window.location.href = result.redirectUrl;
         } else {
-          window.location.href = config.appSubUrl + '/';
+          window.location.href = config.appSubUrl + "/";
         }
       });
     };
@@ -82,4 +83,4 @@ export class LoginCtrl {
   }
 }
 
-coreModule.controller('LoginCtrl', LoginCtrl);
+coreModule.controller("LoginCtrl", LoginCtrl);

+ 22 - 17
public/app/core/controllers/reset_password_ctrl.ts

@@ -1,26 +1,25 @@
-import coreModule from '../core_module';
+import coreModule from "../core_module";
 
 export class ResetPasswordCtrl {
-
   /** @ngInject */
   constructor($scope, contextSrv, backendSrv, $location) {
     contextSrv.sidemenu = false;
     $scope.formModel = {};
-    $scope.mode = 'send';
+    $scope.mode = "send";
 
     var params = $location.search();
     if (params.code) {
-      $scope.mode = 'reset';
+      $scope.mode = "reset";
       $scope.formModel.code = params.code;
     }
 
     $scope.navModel = {
       main: {
-        icon: 'gicon gicon-branding',
-        subTitle: 'Reset your Grafana password',
+        icon: "gicon gicon-branding",
+        subTitle: "Reset your Grafana password",
         breadcrumbs: [
-          { title: 'Login', url: '/login' },
-          { title: 'Reset Password' },
+          { title: "Login", url: "/login" },
+          { title: "Reset Password" }
         ]
       }
     };
@@ -29,24 +28,30 @@ export class ResetPasswordCtrl {
       if (!$scope.sendResetForm.$valid) {
         return;
       }
-      backendSrv.post('/api/user/password/send-reset-email', $scope.formModel).then(function() {
-        $scope.mode = 'email-sent';
-      });
+      backendSrv
+        .post("/api/user/password/send-reset-email", $scope.formModel)
+        .then(function() {
+          $scope.mode = "email-sent";
+        });
     };
 
     $scope.submitReset = function() {
-      if (!$scope.resetForm.$valid) { return; }
+      if (!$scope.resetForm.$valid) {
+        return;
+      }
 
       if ($scope.formModel.newPassword !== $scope.formModel.confirmPassword) {
-        $scope.appEvent('alert-warning', ['New passwords do not match', '']);
+        $scope.appEvent("alert-warning", ["New passwords do not match", ""]);
         return;
       }
 
-      backendSrv.post('/api/user/password/reset', $scope.formModel).then(function() {
-        $location.path('login');
-      });
+      backendSrv
+        .post("/api/user/password/reset", $scope.formModel)
+        .then(function() {
+          $location.path("login");
+        });
     };
   }
 }
 
-coreModule.controller('ResetPasswordCtrl', ResetPasswordCtrl);
+coreModule.controller("ResetPasswordCtrl", ResetPasswordCtrl);

+ 23 - 25
public/app/core/controllers/signup_ctrl.ts

@@ -1,17 +1,16 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import config from 'app/core/config';
-import coreModule from '../core_module';
+import config from "app/core/config";
+import coreModule from "../core_module";
 
 export class SignUpCtrl {
-
   /** @ngInject */
   constructor(
-      private $scope: any,
-      private backendSrv: any,
-      $location: any,
-      contextSrv: any) {
-
+    private $scope: any,
+    private backendSrv: any,
+    $location: any,
+    contextSrv: any
+  ) {
     contextSrv.sidemenu = false;
     $scope.ctrl = this;
 
@@ -28,35 +27,34 @@ export class SignUpCtrl {
 
     $scope.navModel = {
       main: {
-        icon: 'gicon gicon-branding',
-        subTitle: 'Register your Grafana account',
-        breadcrumbs: [
-          { title: 'Login', url: '/login' },
-          { title: 'Sign Up' },
-        ]
+        icon: "gicon gicon-branding",
+        subTitle: "Register your Grafana account",
+        breadcrumbs: [{ title: "Login", url: "/login" }, { title: "Sign Up" }]
       }
     };
 
-    backendSrv.get('/api/user/signup/options').then(options => {
+    backendSrv.get("/api/user/signup/options").then(options => {
       $scope.verifyEmailEnabled = options.verifyEmailEnabled;
       $scope.autoAssignOrg = options.autoAssignOrg;
     });
   }
 
-  submit () {
+  submit() {
     if (!this.$scope.signUpForm.$valid) {
       return;
     }
 
-    this.backendSrv.post('/api/user/signup/step2', this.$scope.formModel).then(rsp => {
-      if (rsp.code === 'redirect-to-select-org') {
-        window.location.href = config.appSubUrl + '/profile/select-org?signup=1';
-      } else {
-        window.location.href = config.appSubUrl + '/';
-      }
-    });
+    this.backendSrv
+      .post("/api/user/signup/step2", this.$scope.formModel)
+      .then(rsp => {
+        if (rsp.code === "redirect-to-select-org") {
+          window.location.href =
+            config.appSubUrl + "/profile/select-org?signup=1";
+        } else {
+          window.location.href = config.appSubUrl + "/";
+        }
+      });
   }
 }
 
-coreModule.controller('SignUpCtrl', SignUpCtrl);
-
+coreModule.controller("SignUpCtrl", SignUpCtrl);

+ 47 - 47
public/app/core/core.ts

@@ -9,54 +9,54 @@ import "./directives/value_select_dropdown";
 import "./directives/rebuild_on_change";
 import "./directives/give_focus";
 import "./directives/diff-view";
-import './jquery_extended';
-import './partials';
-import './components/jsontree/jsontree';
-import './components/code_editor/code_editor';
-import './utils/outline';
-import './components/colorpicker/ColorPicker';
-import './components/colorpicker/SeriesColorPicker';
-import './components/colorpicker/spectrum_picker';
-import './services/search_srv';
-import './services/ng_react';
+import "./jquery_extended";
+import "./partials";
+import "./components/jsontree/jsontree";
+import "./components/code_editor/code_editor";
+import "./utils/outline";
+import "./components/colorpicker/ColorPicker";
+import "./components/colorpicker/SeriesColorPicker";
+import "./components/colorpicker/spectrum_picker";
+import "./services/search_srv";
+import "./services/ng_react";
 
-import {grafanaAppDirective} from './components/grafana_app';
-import {sideMenuDirective} from './components/sidemenu/sidemenu';
-import {searchDirective} from './components/search/search';
-import {infoPopover} from './components/info_popover';
-import {navbarDirective} from './components/navbar/navbar';
-import {arrayJoin} from './directives/array_join';
-import {liveSrv} from './live/live_srv';
-import {Emitter} from './utils/emitter';
-import {layoutSelector} from './components/layout_selector/layout_selector';
-import {switchDirective} from './components/switch';
-import {dashboardSelector} from './components/dashboard_selector';
-import {queryPartEditorDirective} from './components/query_part/query_part_editor';
-import {formDropdownDirective} from './components/form_dropdown/form_dropdown';
-import 'app/core/controllers/all';
-import 'app/core/services/all';
-import 'app/core/routes/routes';
-import './filters/filters';
-import coreModule from './core_module';
-import appEvents from './app_events';
-import colors from './utils/colors';
-import {assignModelProperties} from './utils/model_utils';
-import {contextSrv} from './services/context_srv';
-import {KeybindingSrv} from './services/keybindingSrv';
-import {helpModal} from './components/help/help';
-import {JsonExplorer} from './components/json_explorer/json_explorer';
-import {NavModelSrv, NavModel} from './nav_model_srv';
-import {userPicker} from './components/user_picker';
-import {teamPicker} from './components/team_picker';
-import {geminiScrollbar} from './components/scroll/scroll';
-import {gfPageDirective} from './components/gf_page';
-import {orgSwitcher} from './components/org_switcher';
-import {profiler} from './profiler';
-import {registerAngularDirectives} from './angular_wrappers';
-import {updateLegendValues} from './time_series2';
-import TimeSeries from './time_series2';
-import {searchResultsDirective} from './components/search/search_results';
-import {manageDashboardsDirective} from './components/manage_dashboards/manage_dashboards';
+import { grafanaAppDirective } from "./components/grafana_app";
+import { sideMenuDirective } from "./components/sidemenu/sidemenu";
+import { searchDirective } from "./components/search/search";
+import { infoPopover } from "./components/info_popover";
+import { navbarDirective } from "./components/navbar/navbar";
+import { arrayJoin } from "./directives/array_join";
+import { liveSrv } from "./live/live_srv";
+import { Emitter } from "./utils/emitter";
+import { layoutSelector } from "./components/layout_selector/layout_selector";
+import { switchDirective } from "./components/switch";
+import { dashboardSelector } from "./components/dashboard_selector";
+import { queryPartEditorDirective } from "./components/query_part/query_part_editor";
+import { formDropdownDirective } from "./components/form_dropdown/form_dropdown";
+import "app/core/controllers/all";
+import "app/core/services/all";
+import "app/core/routes/routes";
+import "./filters/filters";
+import coreModule from "./core_module";
+import appEvents from "./app_events";
+import colors from "./utils/colors";
+import { assignModelProperties } from "./utils/model_utils";
+import { contextSrv } from "./services/context_srv";
+import { KeybindingSrv } from "./services/keybindingSrv";
+import { helpModal } from "./components/help/help";
+import { JsonExplorer } from "./components/json_explorer/json_explorer";
+import { NavModelSrv, NavModel } from "./nav_model_srv";
+import { userPicker } from "./components/user_picker";
+import { teamPicker } from "./components/team_picker";
+import { geminiScrollbar } from "./components/scroll/scroll";
+import { gfPageDirective } from "./components/gf_page";
+import { orgSwitcher } from "./components/org_switcher";
+import { profiler } from "./profiler";
+import { registerAngularDirectives } from "./angular_wrappers";
+import { updateLegendValues } from "./time_series2";
+import TimeSeries from "./time_series2";
+import { searchResultsDirective } from "./components/search/search_results";
+import { manageDashboardsDirective } from "./components/manage_dashboards/manage_dashboards";
 
 export {
   profiler,

+ 8 - 10
public/app/core/directives/array_join.ts

@@ -1,23 +1,22 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import coreModule from '../core_module';
+import _ from "lodash";
+import coreModule from "../core_module";
 
 export function arrayJoin() {
-  'use strict';
+  "use strict";
 
   return {
-    restrict: 'A',
-    require: 'ngModel',
+    restrict: "A",
+    require: "ngModel",
     link: function(scope, element, attr, ngModel) {
-
       function split_array(text) {
-        return (text || '').split(',');
+        return (text || "").split(",");
       }
 
       function join_array(text) {
         if (_.isArray(text)) {
-          return (text || '').join(',');
+          return (text || "").join(",");
         } else {
           return text;
         }
@@ -29,5 +28,4 @@ export function arrayJoin() {
   };
 }
 
-coreModule.directive('arrayJoin', arrayJoin);
-
+coreModule.directive("arrayJoin", arrayJoin);

+ 16 - 17
public/app/core/directives/diff-view.ts

@@ -1,17 +1,16 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import angular from 'angular';
-import coreModule from '../core_module';
+import angular from "angular";
+import coreModule from "../core_module";
 
 export class DeltaCtrl {
   observer: any;
 
   /** @ngInject */
   constructor(private $rootScope) {
-
-    const waitForCompile = (mutations) => {
+    const waitForCompile = mutations => {
       if (mutations.length === 1) {
-        this.$rootScope.appEvent('json-diff-ready');
+        this.$rootScope.appEvent("json-diff-ready");
       }
     };
 
@@ -19,13 +18,13 @@ export class DeltaCtrl {
 
     const observerConfig = {
       attributes: true,
-      attributeFilter: ['class'],
+      attributeFilter: ["class"],
       characterData: false,
       childList: true,
-      subtree: false,
+      subtree: false
     };
 
-    this.observer.observe(angular.element('.delta-html')[0], observerConfig);
+    this.observer.observe(angular.element(".delta-html")[0], observerConfig);
   }
 
   $onDestroy() {
@@ -37,10 +36,10 @@ export function delta() {
   return {
     controller: DeltaCtrl,
     replace: false,
-    restrict: 'A',
+    restrict: "A"
   };
 }
-coreModule.directive('diffDelta', delta);
+coreModule.directive("diffDelta", delta);
 
 // Link to JSON line number
 export class LinkJSONCtrl {
@@ -56,7 +55,7 @@ export class LinkJSONCtrl {
     };
 
     this.$scope.switchView().then(() => {
-      unbind = this.$rootScope.$on('json-diff-ready', scroll.bind(this));
+      unbind = this.$rootScope.$on("json-diff-ready", scroll.bind(this));
     });
   }
 }
@@ -64,15 +63,15 @@ export class LinkJSONCtrl {
 export function linkJson() {
   return {
     controller: LinkJSONCtrl,
-    controllerAs: 'ctrl',
+    controllerAs: "ctrl",
     replace: true,
-    restrict: 'E',
+    restrict: "E",
     scope: {
-      line: '@lineDisplay',
-      link: '@lineLink',
-      switchView: '&',
+      line: "@lineDisplay",
+      link: "@lineLink",
+      switchView: "&"
     },
     template: `<a class="diff-linenum btn btn-inverse btn-small" ng-click="ctrl.goToLine(link)">Line {{ line }}</a>`
   };
 }
-coreModule.directive('diffLinkJson', linkJson);
+coreModule.directive("diffLinkJson", linkJson);

+ 18 - 14
public/app/core/directives/give_focus.ts

@@ -1,26 +1,30 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import coreModule from '../core_module';
+import coreModule from "../core_module";
 
-coreModule.directive('giveFocus', function() {
+coreModule.directive("giveFocus", function() {
   return function(scope, element, attrs) {
     element.click(function(e) {
       e.stopPropagation();
     });
 
-    scope.$watch(attrs.giveFocus, function (newValue) {
-      if (!newValue) {
-        return;
-      }
-      setTimeout(function() {
-        element.focus();
-        var domEl = element[0];
-        if (domEl.setSelectionRange) {
-          var pos = element.val().length * 2;
-          domEl.setSelectionRange(pos, pos);
+    scope.$watch(
+      attrs.giveFocus,
+      function(newValue) {
+        if (!newValue) {
+          return;
         }
-      }, 200);
-    }, true);
+        setTimeout(function() {
+          element.focus();
+          var domEl = element[0];
+          if (domEl.setSelectionRange) {
+            var pos = element.val().length * 2;
+            domEl.setSelectionRange(pos, pos);
+          }
+        }, 200);
+      },
+      true
+    );
   };
 });
 

+ 19 - 17
public/app/core/directives/ng_model_on_blur.ts

@@ -1,18 +1,18 @@
-import coreModule from '../core_module';
-import * as rangeUtil from 'app/core/utils/rangeutil';
+import coreModule from "../core_module";
+import * as rangeUtil from "app/core/utils/rangeutil";
 
 function ngModelOnBlur() {
   return {
-    restrict: 'A',
+    restrict: "A",
     priority: 1,
-    require: 'ngModel',
+    require: "ngModel",
     link: function(scope, elm, attr, ngModelCtrl) {
-      if (attr.type === 'radio' || attr.type === 'checkbox') {
+      if (attr.type === "radio" || attr.type === "checkbox") {
         return;
       }
 
-      elm.off('input keydown change');
-      elm.bind('blur', function() {
+      elm.off("input keydown change");
+      elm.bind("blur", function() {
         scope.$apply(function() {
           ngModelCtrl.$setViewValue(elm.val());
         });
@@ -23,11 +23,13 @@ function ngModelOnBlur() {
 
 function emptyToNull() {
   return {
-    restrict: 'A',
-    require: 'ngModel',
-    link: function (scope, elm, attrs, ctrl) {
-      ctrl.$parsers.push(function (viewValue) {
-        if (viewValue === "") { return null; }
+    restrict: "A",
+    require: "ngModel",
+    link: function(scope, elm, attrs, ctrl) {
+      ctrl.$parsers.push(function(viewValue) {
+        if (viewValue === "") {
+          return null;
+        }
         return viewValue;
       });
     }
@@ -36,13 +38,13 @@ function emptyToNull() {
 
 function validTimeSpan() {
   return {
-    require: 'ngModel',
+    require: "ngModel",
     link: function(scope, elm, attrs, ctrl) {
       ctrl.$validators.integer = function(modelValue, viewValue) {
         if (ctrl.$isEmpty(modelValue)) {
           return true;
         }
-        if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) {
+        if (viewValue.indexOf("$") === 0 || viewValue.indexOf("+$") === 0) {
           return true; // allow template variable
         }
         var info = rangeUtil.describeTextRange(viewValue);
@@ -52,6 +54,6 @@ function validTimeSpan() {
   };
 }
 
-coreModule.directive('ngModelOnblur', ngModelOnBlur);
-coreModule.directive('emptyToNull', emptyToNull);
-coreModule.directive('validTimeSpan', validTimeSpan);
+coreModule.directive("ngModelOnblur", ngModelOnBlur);
+coreModule.directive("emptyToNull", emptyToNull);
+coreModule.directive("validTimeSpan", validTimeSpan);

+ 12 - 8
public/app/core/directives/rebuild_on_change.ts

@@ -1,5 +1,5 @@
-import $ from 'jquery';
-import coreModule from '../core_module';
+import $ from "jquery";
+import coreModule from "../core_module";
 
 function getBlockNodes(nodes) {
   var node = nodes[0];
@@ -20,13 +20,12 @@ function getBlockNodes(nodes) {
 
 /** @ngInject **/
 function rebuildOnChange($animate) {
-
   return {
     multiElement: true,
     terminal: true,
     transclude: true,
     priority: 600,
-    restrict: 'E',
+    restrict: "E",
     link: function(scope, elem, attrs, ctrl, transclude) {
       var block, childScope, previousElements;
 
@@ -48,7 +47,10 @@ function rebuildOnChange($animate) {
         }
       }
 
-      scope.$watch(attrs.property, function rebuildOnChangeAction(value, oldValue) {
+      scope.$watch(attrs.property, function rebuildOnChangeAction(
+        value,
+        oldValue
+      ) {
         if (childScope && value !== oldValue) {
           cleanUp();
         }
@@ -56,8 +58,10 @@ function rebuildOnChange($animate) {
         if (!childScope && (value || attrs.showNull)) {
           transclude(function(clone, newScope) {
             childScope = newScope;
-            clone[clone.length++] = document.createComment(' end rebuild on change ');
-            block = {clone: clone};
+            clone[clone.length++] = document.createComment(
+              " end rebuild on change "
+            );
+            block = { clone: clone };
             $animate.enter(clone, elem.parent(), elem);
           });
         } else {
@@ -68,4 +72,4 @@ function rebuildOnChange($animate) {
   };
 }
 
-coreModule.directive('rebuildOnChange', rebuildOnChange);
+coreModule.directive("rebuildOnChange", rebuildOnChange);

+ 102 - 44
public/app/core/directives/tags.ts

@@ -1,12 +1,12 @@
-import angular from 'angular';
-import $ from 'jquery';
-import coreModule from '../core_module';
-import 'vendor/tagsinput/bootstrap-tagsinput.js';
+import angular from "angular";
+import $ from "jquery";
+import coreModule from "../core_module";
+import "vendor/tagsinput/bootstrap-tagsinput.js";
 
 function djb2(str) {
   var hash = 5381;
   for (var i = 0; i < str.length; i++) {
-    hash = ((hash << 5) + hash) + str.charCodeAt(i); /* hash * 33 + c */
+    hash = (hash << 5) + hash + str.charCodeAt(i); /* hash * 33 + c */
   }
   return hash;
 }
@@ -14,20 +14,66 @@ function djb2(str) {
 function setColor(name, element) {
   var hash = djb2(name.toLowerCase());
   var colors = [
-    "#E24D42","#1F78C1","#BA43A9","#705DA0","#466803",
-    "#508642","#447EBC","#C15C17","#890F02","#757575",
-    "#0A437C","#6D1F62","#584477","#629E51","#2F4F4F",
-    "#BF1B00","#806EB7","#8a2eb8", "#699e00","#000000",
-    "#3F6833","#2F575E","#99440A","#E0752D","#0E4AB4",
-    "#58140C","#052B51","#511749","#3F2B5B",
+    "#E24D42",
+    "#1F78C1",
+    "#BA43A9",
+    "#705DA0",
+    "#466803",
+    "#508642",
+    "#447EBC",
+    "#C15C17",
+    "#890F02",
+    "#757575",
+    "#0A437C",
+    "#6D1F62",
+    "#584477",
+    "#629E51",
+    "#2F4F4F",
+    "#BF1B00",
+    "#806EB7",
+    "#8a2eb8",
+    "#699e00",
+    "#000000",
+    "#3F6833",
+    "#2F575E",
+    "#99440A",
+    "#E0752D",
+    "#0E4AB4",
+    "#58140C",
+    "#052B51",
+    "#511749",
+    "#3F2B5B"
   ];
   var borderColors = [
-    "#FF7368","#459EE7","#E069CF","#9683C6","#6C8E29",
-    "#76AC68","#6AA4E2","#E7823D","#AF3528","#9B9B9B",
-    "#3069A2","#934588","#7E6A9D","#88C477","#557575",
-    "#E54126","#A694DD","#B054DE", "#8FC426","#262626",
-    "#658E59","#557D84","#BF6A30","#FF9B53","#3470DA",
-    "#7E3A32","#2B5177","#773D6F","#655181",
+    "#FF7368",
+    "#459EE7",
+    "#E069CF",
+    "#9683C6",
+    "#6C8E29",
+    "#76AC68",
+    "#6AA4E2",
+    "#E7823D",
+    "#AF3528",
+    "#9B9B9B",
+    "#3069A2",
+    "#934588",
+    "#7E6A9D",
+    "#88C477",
+    "#557575",
+    "#E54126",
+    "#A694DD",
+    "#B054DE",
+    "#8FC426",
+    "#262626",
+    "#658E59",
+    "#557D84",
+    "#BF6A30",
+    "#FF9B53",
+    "#3470DA",
+    "#7E3A32",
+    "#2B5177",
+    "#773D6F",
+    "#655181"
   ];
   var color = colors[Math.abs(hash % colors.length)];
   var borderColor = borderColors[Math.abs(hash % borderColors.length)];
@@ -38,7 +84,7 @@ function setColor(name, element) {
 function tagColorFromName() {
   return {
     scope: { tagColorFromName: "=" },
-    link: function (scope, element) {
+    link: function(scope, element) {
       setColor(scope.tagColorFromName, element);
     }
   };
@@ -60,48 +106,57 @@ function bootstrapTagsinput() {
   }
 
   return {
-    restrict: 'EA',
+    restrict: "EA",
     scope: {
-      model: '=ngModel',
-      onTagsUpdated: "&",
+      model: "=ngModel",
+      onTagsUpdated: "&"
     },
-    template: '<select multiple></select>',
+    template: "<select multiple></select>",
     replace: false,
     link: function(scope, element, attrs) {
-
       if (!angular.isArray(scope.model)) {
         scope.model = [];
       }
 
-      var select = $('select', element);
+      var select = $("select", element);
 
       if (attrs.placeholder) {
-        select.attr('placeholder', attrs.placeholder);
+        select.attr("placeholder", attrs.placeholder);
       }
 
       select.tagsinput({
         typeahead: {
-          source: angular.isFunction(scope.$parent[attrs.typeaheadSource]) ? scope.$parent[attrs.typeaheadSource] : null
+          source: angular.isFunction(scope.$parent[attrs.typeaheadSource])
+            ? scope.$parent[attrs.typeaheadSource]
+            : null
         },
         widthClass: attrs.widthClass,
         itemValue: getItemProperty(scope, attrs.itemvalue),
-        itemText : getItemProperty(scope, attrs.itemtext),
-        tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ?
-          scope.$parent[attrs.tagclass] : function() { return attrs.tagclass; }
+        itemText: getItemProperty(scope, attrs.itemtext),
+        tagClass: angular.isFunction(scope.$parent[attrs.tagclass])
+          ? scope.$parent[attrs.tagclass]
+          : function() {
+              return attrs.tagclass;
+            }
       });
 
-      select.on('itemAdded', function(event) {
+      select.on("itemAdded", function(event) {
         if (scope.model.indexOf(event.item) === -1) {
           scope.model.push(event.item);
           if (scope.onTagsUpdated) {
             scope.onTagsUpdated();
           }
         }
-        var tagElement = select.next().children("span").filter(function() { return $(this).text() === event.item; });
+        var tagElement = select
+          .next()
+          .children("span")
+          .filter(function() {
+            return $(this).text() === event.item;
+          });
         setColor(event.item, tagElement);
       });
 
-      select.on('itemRemoved', function(event) {
+      select.on("itemRemoved", function(event) {
         var idx = scope.model.indexOf(event.item);
         if (idx !== -1) {
           scope.model.splice(idx, 1);
@@ -111,21 +166,24 @@ function bootstrapTagsinput() {
         }
       });
 
-      scope.$watch("model", function() {
-        if (!angular.isArray(scope.model)) {
-          scope.model = [];
-        }
+      scope.$watch(
+        "model",
+        function() {
+          if (!angular.isArray(scope.model)) {
+            scope.model = [];
+          }
 
-        select.tagsinput('removeAll');
+          select.tagsinput("removeAll");
 
-        for (var i = 0; i < scope.model.length; i++) {
-          select.tagsinput('add', scope.model[i]);
-        }
-
-      }, true);
+          for (var i = 0; i < scope.model.length; i++) {
+            select.tagsinput("add", scope.model[i]);
+          }
+        },
+        true
+      );
     }
   };
 }
 
-coreModule.directive('tagColorFromName', tagColorFromName);
-coreModule.directive('bootstrapTagsinput', bootstrapTagsinput);
+coreModule.directive("tagColorFromName", tagColorFromName);
+coreModule.directive("bootstrapTagsinput", bootstrapTagsinput);

+ 20 - 22
public/app/core/filters/filters.ts

@@ -1,17 +1,17 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import angular from 'angular';
-import moment from 'moment';
-import coreModule from '../core_module';
+import _ from "lodash";
+import angular from "angular";
+import moment from "moment";
+import coreModule from "../core_module";
 
-coreModule.filter('stringSort', function() {
+coreModule.filter("stringSort", function() {
   return function(input) {
     return input.sort();
   };
 });
 
-coreModule.filter('slice', function() {
+coreModule.filter("slice", function() {
   return function(arr, start, end) {
     if (!_.isUndefined(arr)) {
       return arr.slice(start, end);
@@ -19,7 +19,7 @@ coreModule.filter('slice', function() {
   };
 });
 
-coreModule.filter('stringify', function() {
+coreModule.filter("stringify", function() {
   return function(arr) {
     if (_.isObject(arr) && !_.isArray(arr)) {
       return angular.toJson(arr);
@@ -29,31 +29,29 @@ coreModule.filter('stringify', function() {
   };
 });
 
-coreModule.filter('moment', function() {
+coreModule.filter("moment", function() {
   return function(date, mode) {
     switch (mode) {
-      case 'ago':
+      case "ago":
         return moment(date).fromNow();
     }
     return moment(date).fromNow();
   };
 });
 
-coreModule.filter('noXml', function() {
+coreModule.filter("noXml", function() {
   var noXml = function(text) {
-  return _.isString(text)
-    ? text
-    .replace(/&/g, '&amp;')
-    .replace(/</g, '&lt;')
-    .replace(/>/g, '&gt;')
-    .replace(/'/g, '&#39;')
-    .replace(/"/g, '&quot;')
-    : text;
+    return _.isString(text)
+      ? text
+          .replace(/&/g, "&amp;")
+          .replace(/</g, "&lt;")
+          .replace(/>/g, "&gt;")
+          .replace(/'/g, "&#39;")
+          .replace(/"/g, "&quot;")
+      : text;
   };
   return function(text) {
-    return _.isArray(text)
-      ? _.map(text, noXml)
-      : noXml(text);
+    return _.isArray(text) ? _.map(text, noXml) : noXml(text);
   };
 });
 
@@ -74,5 +72,5 @@ function interpolateTemplateVars(templateSrv) {
   return filterFunc;
 }
 
-coreModule.filter('interpolateTemplateVars', interpolateTemplateVars);
+coreModule.filter("interpolateTemplateVars", interpolateTemplateVars);
 export default {};

+ 24 - 20
public/app/core/live/live_srv.ts

@@ -1,7 +1,7 @@
-import _ from 'lodash';
-import config from 'app/core/config';
+import _ from "lodash";
+import config from "app/core/config";
 
-import {Observable} from 'rxjs/Observable';
+import { Observable } from "rxjs/Observable";
 
 export class LiveSrv {
   conn: any;
@@ -14,7 +14,12 @@ export class LiveSrv {
 
   getWebSocketUrl() {
     var l = window.location;
-    return ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + config.appSubUrl + '/ws';
+    return (
+      (l.protocol === "https:" ? "wss://" : "ws://") +
+      l.host +
+      config.appSubUrl +
+      "/ws"
+    );
   }
 
   getConnection() {
@@ -27,29 +32,29 @@ export class LiveSrv {
     }
 
     this.initPromise = new Promise((resolve, reject) => {
-      console.log('Live: connecting...');
+      console.log("Live: connecting...");
       this.conn = new WebSocket(this.getWebSocketUrl());
 
-      this.conn.onclose = (evt) => {
+      this.conn.onclose = evt => {
         console.log("Live: websocket onclose", evt);
-        reject({message: 'Connection closed'});
+        reject({ message: "Connection closed" });
 
         this.initPromise = null;
         setTimeout(this.reconnect.bind(this), 2000);
       };
 
-      this.conn.onmessage = (evt) => {
+      this.conn.onmessage = evt => {
         this.handleMessage(evt.data);
       };
 
-      this.conn.onerror = (evt) => {
+      this.conn.onerror = evt => {
         this.initPromise = null;
-        reject({message: 'Connection error'});
+        reject({ message: "Connection error" });
         console.log("Live: websocket error", evt);
       };
 
-      this.conn.onopen = (evt) => {
-        console.log('opened');
+      this.conn.onopen = evt => {
+        console.log("opened");
         this.initPromise = null;
         resolve(this.conn);
       };
@@ -81,11 +86,11 @@ export class LiveSrv {
       return;
     }
 
-    console.log('LiveSrv: Reconnecting');
+    console.log("LiveSrv: Reconnecting");
 
     this.getConnection().then(conn => {
       _.each(this.observers, (value, key) => {
-        this.send({action: 'subscribe', stream: key});
+        this.send({ action: "subscribe", stream: key });
       });
     });
   }
@@ -98,21 +103,21 @@ export class LiveSrv {
     this.observers[stream] = observer;
 
     this.getConnection().then(conn => {
-      this.send({action: 'subscribe', stream: stream});
+      this.send({ action: "subscribe", stream: stream });
     });
   }
 
   removeObserver(stream, observer) {
-    console.log('unsubscribe', stream);
+    console.log("unsubscribe", stream);
     delete this.observers[stream];
 
     this.getConnection().then(conn => {
-      this.send({action: 'unsubscribe', stream: stream});
+      this.send({ action: "unsubscribe", stream: stream });
     });
   }
 
   subscribe(streamName) {
-    console.log('LiveSrv.subscribe: ' + streamName);
+    console.log("LiveSrv.subscribe: " + streamName);
 
     return Observable.create(observer => {
       this.addObserver(streamName, observer);
@@ -126,8 +131,7 @@ export class LiveSrv {
     //   this.send({action: 'subscribe', stream: name});
     // });
   }
-
 }
 
 var instance = new LiveSrv();
-export {instance as liveSrv};
+export { instance as liveSrv };

+ 2 - 6
public/app/core/mod_defs.d.ts

@@ -1,18 +1,14 @@
 declare module "app/core/controllers/all" {
   let json: any;
-  export {json};
+  export { json };
 }
 
 declare module "app/core/routes/all" {
   let json: any;
-  export {json};
+  export { json };
 }
 
 declare module "app/core/services/all" {
   let json: any;
   export default json;
 }
-
-
-
-

+ 6 - 6
public/app/core/nav_model_srv.ts

@@ -1,6 +1,6 @@
-import coreModule from 'app/core/core_module';
-import config from 'app/core/config';
-import _ from 'lodash';
+import coreModule from "app/core/core_module";
+import config from "app/core/config";
+import _ from "lodash";
 
 export interface NavModelItem {
   text: string;
@@ -34,7 +34,7 @@ export class NavModelSrv {
   }
 
   getCfgNode() {
-    return _.find(this.navItems, {id: 'cfg'});
+    return _.find(this.navItems, { id: "cfg" });
   }
 
   getNav(...args) {
@@ -48,7 +48,7 @@ export class NavModelSrv {
         break;
       }
 
-      let node = _.find(children, {id: id});
+      let node = _.find(children, { id: id });
       nav.breadcrumbs.push(node);
       nav.node = node;
       nav.main = node;
@@ -83,4 +83,4 @@ export class NavModelSrv {
   }
 }
 
-coreModule.service('navModelSrv', NavModelSrv);
+coreModule.service("navModelSrv", NavModelSrv);

+ 56 - 30
public/app/core/profiler.ts

@@ -1,5 +1,5 @@
-import $ from 'jquery';
-import angular from 'angular';
+import $ from "jquery";
+import angular from "angular";
 
 export class Profiler {
   panelsRendered: number;
@@ -11,7 +11,7 @@ export class Profiler {
   scopeCount: any;
 
   init(config, $rootScope) {
-    this.enabled = config.buildInfo.env === 'development';
+    this.enabled = config.buildInfo.env === "development";
     this.timings = {};
     this.timings.appStart = { loadStart: new Date().getTime() };
     this.$rootScope = $rootScope;
@@ -20,15 +20,30 @@ export class Profiler {
       return;
     }
 
-    $rootScope.$watch(() => {
-      this.digestCounter++;
-      return false;
-    }, () => {});
-
-    $rootScope.onAppEvent('refresh', this.refresh.bind(this), $rootScope);
-    $rootScope.onAppEvent('dashboard-fetch-end', this.dashboardFetched.bind(this), $rootScope);
-    $rootScope.onAppEvent('dashboard-initialized', this.dashboardInitialized.bind(this), $rootScope);
-    $rootScope.onAppEvent('panel-initialized', this.panelInitialized.bind(this), $rootScope);
+    $rootScope.$watch(
+      () => {
+        this.digestCounter++;
+        return false;
+      },
+      () => {}
+    );
+
+    $rootScope.onAppEvent("refresh", this.refresh.bind(this), $rootScope);
+    $rootScope.onAppEvent(
+      "dashboard-fetch-end",
+      this.dashboardFetched.bind(this),
+      $rootScope
+    );
+    $rootScope.onAppEvent(
+      "dashboard-initialized",
+      this.dashboardInitialized.bind(this),
+      $rootScope
+    );
+    $rootScope.onAppEvent(
+      "panel-initialized",
+      this.panelInitialized.bind(this),
+      $rootScope
+    );
   }
 
   refresh() {
@@ -36,10 +51,10 @@ export class Profiler {
     this.timings.render = 0;
 
     setTimeout(() => {
-      console.log('panel count: ' + this.panelsInitCount);
-      console.log('total query: ' + this.timings.query);
-      console.log('total render: ' + this.timings.render);
-      console.log('avg render: ' + this.timings.render / this.panelsInitCount);
+      console.log("panel count: " + this.panelsInitCount);
+      console.log("total query: " + this.timings.query);
+      console.log("total render: " + this.timings.render);
+      console.log("avg render: " + this.timings.render / this.panelsInitCount);
     }, 5000);
   }
 
@@ -55,12 +70,21 @@ export class Profiler {
 
   dashboardInitialized() {
     setTimeout(() => {
-      console.log("Dashboard::Performance Total Digests: " + this.digestCounter);
-      console.log("Dashboard::Performance Total Watchers: " + this.getTotalWatcherCount());
-      console.log("Dashboard::Performance Total ScopeCount: " + this.scopeCount);
-
-      var timeTaken = this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
-      console.log("Dashboard::Performance All panels initialized in " + timeTaken + " ms");
+      console.log(
+        "Dashboard::Performance Total Digests: " + this.digestCounter
+      );
+      console.log(
+        "Dashboard::Performance Total Watchers: " + this.getTotalWatcherCount()
+      );
+      console.log(
+        "Dashboard::Performance Total ScopeCount: " + this.scopeCount
+      );
+
+      var timeTaken =
+        this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
+      console.log(
+        "Dashboard::Performance All panels initialized in " + timeTaken + " ms"
+      );
 
       // measure digest performance
       var rootDigestStart = window.performance.now();
@@ -68,24 +92,27 @@ export class Profiler {
         this.$rootScope.$apply();
       }
 
-      console.log("Dashboard::Performance Root Digest " + ((window.performance.now() - rootDigestStart) / 30));
+      console.log(
+        "Dashboard::Performance Root Digest " +
+          (window.performance.now() - rootDigestStart) / 30
+      );
     }, 3000);
   }
 
   getTotalWatcherCount() {
     var count = 0;
     var scopes = 0;
-    var root = $(document.getElementsByTagName('body'));
+    var root = $(document.getElementsByTagName("body"));
 
-    var f = function (element) {
-      if (element.data().hasOwnProperty('$scope')) {
+    var f = function(element) {
+      if (element.data().hasOwnProperty("$scope")) {
         scopes++;
-        angular.forEach(element.data().$scope.$$watchers, function () {
+        angular.forEach(element.data().$scope.$$watchers, function() {
           count++;
         });
       }
 
-      angular.forEach(element.children(), function (childElement) {
+      angular.forEach(element.children(), function(childElement) {
         f($(childElement));
       });
     };
@@ -116,8 +143,7 @@ export class Profiler {
     this.panelsInitCount++;
     this.timings.lastPanelInitializedAt = new Date().getTime();
   }
-
 }
 
 var profiler = new Profiler();
-export {profiler};
+export { profiler };

+ 15 - 11
public/app/core/routes/bundle_loader.ts

@@ -4,19 +4,23 @@ export class BundleLoader {
   constructor(bundleName) {
     var defer = null;
 
-    this.lazy = ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
-      if (defer) {
-        return defer.promise;
-      }
-
-      defer = $q.defer();
+    this.lazy = [
+      "$q",
+      "$route",
+      "$rootScope",
+      ($q, $route, $rootScope) => {
+        if (defer) {
+          return defer.promise;
+        }
 
-      System.import(bundleName).then(() => {
-        defer.resolve();
-      });
+        defer = $q.defer();
 
-      return defer.promise;
-    }];
+        System.import(bundleName).then(() => {
+          defer.resolve();
+        });
 
+        return defer.promise;
+      }
+    ];
   }
 }

+ 29 - 26
public/app/core/routes/dashboard_loaders.ts

@@ -1,15 +1,14 @@
-import coreModule from '../core_module';
+import coreModule from "../core_module";
 
 export class LoadDashboardCtrl {
-
   /** @ngInject */
   constructor($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) {
     $scope.appEvent("dashboard-fetch-start");
 
     if (!$routeParams.slug) {
-      backendSrv.get('/api/dashboards/home').then(function(homeDash) {
+      backendSrv.get("/api/dashboards/home").then(function(homeDash) {
         if (homeDash.redirectUri) {
-          $location.path('dashboard/' + homeDash.redirectUri);
+          $location.path("dashboard/" + homeDash.redirectUri);
         } else {
           var meta = homeDash.meta;
           meta.canSave = meta.canShare = meta.canStar = false;
@@ -19,35 +18,39 @@ export class LoadDashboardCtrl {
       return;
     }
 
-    dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) {
-      if ($routeParams.keepRows) {
-        result.meta.keepRows = true;
-      }
-      $scope.initDashboard(result, $scope);
-    });
+    dashboardLoaderSrv
+      .loadDashboard($routeParams.type, $routeParams.slug)
+      .then(function(result) {
+        if ($routeParams.keepRows) {
+          result.meta.keepRows = true;
+        }
+        $scope.initDashboard(result, $scope);
+      });
   }
 }
 
 export class NewDashboardCtrl {
-
   /** @ngInject */
   constructor($scope, $routeParams) {
-    $scope.initDashboard({
-      meta: { canStar: false, canShare: false, isNew: true },
-      dashboard: {
-        title: "New dashboard",
-        panels: [
-          {
-            type: 'add-panel',
-            gridPos: {x: 0, y: 0, w: 12, h: 9},
-            title: 'Panel Title',
-          }
-        ],
-        folderId: Number($routeParams.folderId)
+    $scope.initDashboard(
+      {
+        meta: { canStar: false, canShare: false, isNew: true },
+        dashboard: {
+          title: "New dashboard",
+          panels: [
+            {
+              type: "add-panel",
+              gridPos: { x: 0, y: 0, w: 12, h: 9 },
+              title: "Panel Title"
+            }
+          ],
+          folderId: Number($routeParams.folderId)
+        }
       },
-    }, $scope);
+      $scope
+    );
   }
 }
 
-coreModule.controller('LoadDashboardCtrl', LoadDashboardCtrl);
-coreModule.controller('NewDashboardCtrl', NewDashboardCtrl);
+coreModule.controller("LoadDashboardCtrl", LoadDashboardCtrl);
+coreModule.controller("NewDashboardCtrl", NewDashboardCtrl);

+ 286 - 264
public/app/core/routes/routes.ts

@@ -1,282 +1,304 @@
-import './dashboard_loaders';
-import coreModule from 'app/core/core_module';
+import "./dashboard_loaders";
+import coreModule from "app/core/core_module";
 
 /** @ngInject **/
 function setupAngularRoutes($routeProvider, $locationProvider) {
   $locationProvider.html5Mode(true);
 
   var loadOrgBundle = {
-    lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
-      return System.import('app/features/org/all');
-    }]
+    lazy: [
+      "$q",
+      "$route",
+      "$rootScope",
+      ($q, $route, $rootScope) => {
+        return System.import("app/features/org/all");
+      }
+    ]
   };
 
   var loadAdminBundle = {
-    lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
-      return System.import('app/features/admin/admin');
-    }]
+    lazy: [
+      "$q",
+      "$route",
+      "$rootScope",
+      ($q, $route, $rootScope) => {
+        return System.import("app/features/admin/admin");
+      }
+    ]
   };
 
   var loadAlertingBundle = {
-    lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
-      return System.import('app/features/alerting/all');
-    }]
+    lazy: [
+      "$q",
+      "$route",
+      "$rootScope",
+      ($q, $route, $rootScope) => {
+        return System.import("app/features/alerting/all");
+      }
+    ]
   };
 
   $routeProvider
-  .when('/', {
-    templateUrl: 'public/app/partials/dashboard.html',
-    controller : 'LoadDashboardCtrl',
-    reloadOnSearch: false,
-    pageClass: 'page-dashboard',
-  })
-  .when('/dashboard/:type/:slug', {
-    templateUrl: 'public/app/partials/dashboard.html',
-    controller : 'LoadDashboardCtrl',
-    reloadOnSearch: false,
-    pageClass: 'page-dashboard',
-  })
-  .when('/dashboard-solo/:type/:slug', {
-    templateUrl: 'public/app/features/panel/partials/soloPanel.html',
-    controller : 'SoloPanelCtrl',
-    reloadOnSearch: false,
-    pageClass: 'page-dashboard',
-  })
-  .when('/dashboard/new', {
-    templateUrl: 'public/app/partials/dashboard.html',
-    controller : 'NewDashboardCtrl',
-    reloadOnSearch: false,
-    pageClass: 'page-dashboard',
-  })
-  .when('/dashboard/import', {
-    templateUrl: 'public/app/features/dashboard/partials/dashboardImport.html',
-    controller : 'DashboardImportCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/datasources', {
-    templateUrl: 'public/app/features/plugins/partials/ds_list.html',
-    controller : 'DataSourcesCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/datasources/edit/:id', {
-    templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
-    controller : 'DataSourceEditCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/datasources/new', {
-    templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
-    controller : 'DataSourceEditCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/dashboards', {
-    templateUrl: 'public/app/features/dashboard/partials/dashboard_list.html',
-    controller : 'DashboardListCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/dashboards/folder/new', {
-    templateUrl: 'public/app/features/dashboard/partials/create_folder.html',
-    controller : 'CreateFolderCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/dashboards/folder/:folderId/:slug/permissions', {
-    templateUrl: 'public/app/features/dashboard/partials/folder_permissions.html',
-    controller : 'FolderPermissionsCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/dashboards/folder/:folderId/:slug/settings', {
-    templateUrl: 'public/app/features/dashboard/partials/folder_settings.html',
-    controller : 'FolderSettingsCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/dashboards/folder/:folderId/:slug', {
-    templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html',
-    controller : 'FolderDashboardsCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/org', {
-    templateUrl: 'public/app/features/org/partials/orgDetails.html',
-    controller : 'OrgDetailsCtrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/new', {
-    templateUrl: 'public/app/features/org/partials/newOrg.html',
-    controller : 'NewOrgCtrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/users', {
-    templateUrl: 'public/app/features/org/partials/orgUsers.html',
-    controller : 'OrgUsersCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/users/invite', {
-    templateUrl: 'public/app/features/org/partials/invite.html',
-    controller : 'UserInviteCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/apikeys', {
-    templateUrl: 'public/app/features/org/partials/orgApiKeys.html',
-    controller : 'OrgApiKeysCtrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/teams', {
-    templateUrl: 'public/app/features/org/partials/teams.html',
-    controller : 'TeamsCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/org/teams/edit/:id', {
-    templateUrl: 'public/app/features/org/partials/team_details.html',
-    controller : 'TeamDetailsCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/profile', {
-    templateUrl: 'public/app/features/org/partials/profile.html',
-    controller : 'ProfileCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/profile/password', {
-    templateUrl: 'public/app/features/org/partials/change_password.html',
-    controller : 'ChangePasswordCtrl',
-    resolve: loadOrgBundle,
-  })
-  .when('/profile/select-org', {
-    templateUrl: 'public/app/features/org/partials/select_org.html',
-    controller : 'SelectOrgCtrl',
-    resolve: loadOrgBundle,
-  })
-  // ADMIN
-  .when('/admin', {
-    templateUrl: 'public/app/features/admin/partials/admin_home.html',
-    controller : 'AdminHomeCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/settings', {
-    templateUrl: 'public/app/features/admin/partials/settings.html',
-    controller : 'AdminSettingsCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/users', {
-    templateUrl: 'public/app/features/admin/partials/users.html',
-    controller : 'AdminListUsersCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/users/create', {
-    templateUrl: 'public/app/features/admin/partials/new_user.html',
-    controller : 'AdminEditUserCtrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/users/edit/:id', {
-    templateUrl: 'public/app/features/admin/partials/edit_user.html',
-    controller : 'AdminEditUserCtrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/orgs', {
-    templateUrl: 'public/app/features/admin/partials/orgs.html',
-    controller : 'AdminListOrgsCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/orgs/edit/:id', {
-    templateUrl: 'public/app/features/admin/partials/edit_org.html',
-    controller : 'AdminEditOrgCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  .when('/admin/stats', {
-    templateUrl: 'public/app/features/admin/partials/stats.html',
-    controller : 'AdminStatsCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAdminBundle,
-  })
-  // LOGIN / SIGNUP
-  .when('/login', {
-    templateUrl: 'public/app/partials/login.html',
-    controller : 'LoginCtrl',
-    pageClass: 'login-page sidemenu-hidden',
-  })
-  .when('/invite/:code', {
-    templateUrl: 'public/app/partials/signup_invited.html',
-    controller : 'InvitedCtrl',
-    pageClass: 'sidemenu-hidden',
-  })
-  .when('/signup', {
-    templateUrl: 'public/app/partials/signup_step2.html',
-    controller : 'SignUpCtrl',
-    pageClass: 'sidemenu-hidden',
-  })
-  .when('/user/password/send-reset-email', {
-    templateUrl: 'public/app/partials/reset_password.html',
-    controller : 'ResetPasswordCtrl',
-    pageClass: 'sidemenu-hidden',
-  })
-  .when('/user/password/reset', {
-    templateUrl: 'public/app/partials/reset_password.html',
-    controller : 'ResetPasswordCtrl',
-    pageClass: 'sidemenu-hidden',
-  })
-  .when('/dashboard/snapshots', {
-    templateUrl: 'public/app/features/snapshot/partials/snapshots.html',
-    controller : 'SnapshotsCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/plugins', {
-    templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
-    controller: 'PluginListCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/plugins/:pluginId/edit', {
-    templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
-    controller: 'PluginEditCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/plugins/:pluginId/page/:slug', {
-    templateUrl: 'public/app/features/plugins/partials/plugin_page.html',
-    controller: 'AppPageCtrl',
-    controllerAs: 'ctrl',
-  })
-  .when('/styleguide/:page?', {
-    controller: 'StyleGuideCtrl',
-    controllerAs: 'ctrl',
-    templateUrl: 'public/app/features/styleguide/styleguide.html',
-  })
-  .when('/alerting', {
-    redirectTo: '/alerting/list'
-  })
-  .when('/alerting/list', {
-    templateUrl: 'public/app/features/alerting/partials/alert_list.html',
-    controller: 'AlertListCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAlertingBundle,
-  })
-  .when('/alerting/notifications', {
-    templateUrl: 'public/app/features/alerting/partials/notifications_list.html',
-    controller: 'AlertNotificationsListCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAlertingBundle,
-  })
-  .when('/alerting/notification/new', {
-    templateUrl: 'public/app/features/alerting/partials/notification_edit.html',
-    controller: 'AlertNotificationEditCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAlertingBundle,
-  })
-  .when('/alerting/notification/:id/edit', {
-    templateUrl: 'public/app/features/alerting/partials/notification_edit.html',
-    controller: 'AlertNotificationEditCtrl',
-    controllerAs: 'ctrl',
-    resolve: loadAlertingBundle,
-  })
-  .otherwise({
-    templateUrl: 'public/app/partials/error.html',
-    controller: 'ErrorCtrl'
-  });
+    .when("/", {
+      templateUrl: "public/app/partials/dashboard.html",
+      controller: "LoadDashboardCtrl",
+      reloadOnSearch: false,
+      pageClass: "page-dashboard"
+    })
+    .when("/dashboard/:type/:slug", {
+      templateUrl: "public/app/partials/dashboard.html",
+      controller: "LoadDashboardCtrl",
+      reloadOnSearch: false,
+      pageClass: "page-dashboard"
+    })
+    .when("/dashboard-solo/:type/:slug", {
+      templateUrl: "public/app/features/panel/partials/soloPanel.html",
+      controller: "SoloPanelCtrl",
+      reloadOnSearch: false,
+      pageClass: "page-dashboard"
+    })
+    .when("/dashboard/new", {
+      templateUrl: "public/app/partials/dashboard.html",
+      controller: "NewDashboardCtrl",
+      reloadOnSearch: false,
+      pageClass: "page-dashboard"
+    })
+    .when("/dashboard/import", {
+      templateUrl:
+        "public/app/features/dashboard/partials/dashboardImport.html",
+      controller: "DashboardImportCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/datasources", {
+      templateUrl: "public/app/features/plugins/partials/ds_list.html",
+      controller: "DataSourcesCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/datasources/edit/:id", {
+      templateUrl: "public/app/features/plugins/partials/ds_edit.html",
+      controller: "DataSourceEditCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/datasources/new", {
+      templateUrl: "public/app/features/plugins/partials/ds_edit.html",
+      controller: "DataSourceEditCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/dashboards", {
+      templateUrl: "public/app/features/dashboard/partials/dashboard_list.html",
+      controller: "DashboardListCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/dashboards/folder/new", {
+      templateUrl: "public/app/features/dashboard/partials/create_folder.html",
+      controller: "CreateFolderCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/dashboards/folder/:folderId/:slug/permissions", {
+      templateUrl:
+        "public/app/features/dashboard/partials/folder_permissions.html",
+      controller: "FolderPermissionsCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/dashboards/folder/:folderId/:slug/settings", {
+      templateUrl:
+        "public/app/features/dashboard/partials/folder_settings.html",
+      controller: "FolderSettingsCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/dashboards/folder/:folderId/:slug", {
+      templateUrl:
+        "public/app/features/dashboard/partials/folder_dashboards.html",
+      controller: "FolderDashboardsCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/org", {
+      templateUrl: "public/app/features/org/partials/orgDetails.html",
+      controller: "OrgDetailsCtrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/new", {
+      templateUrl: "public/app/features/org/partials/newOrg.html",
+      controller: "NewOrgCtrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/users", {
+      templateUrl: "public/app/features/org/partials/orgUsers.html",
+      controller: "OrgUsersCtrl",
+      controllerAs: "ctrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/users/invite", {
+      templateUrl: "public/app/features/org/partials/invite.html",
+      controller: "UserInviteCtrl",
+      controllerAs: "ctrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/apikeys", {
+      templateUrl: "public/app/features/org/partials/orgApiKeys.html",
+      controller: "OrgApiKeysCtrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/teams", {
+      templateUrl: "public/app/features/org/partials/teams.html",
+      controller: "TeamsCtrl",
+      controllerAs: "ctrl",
+      resolve: loadOrgBundle
+    })
+    .when("/org/teams/edit/:id", {
+      templateUrl: "public/app/features/org/partials/team_details.html",
+      controller: "TeamDetailsCtrl",
+      controllerAs: "ctrl",
+      resolve: loadOrgBundle
+    })
+    .when("/profile", {
+      templateUrl: "public/app/features/org/partials/profile.html",
+      controller: "ProfileCtrl",
+      controllerAs: "ctrl",
+      resolve: loadOrgBundle
+    })
+    .when("/profile/password", {
+      templateUrl: "public/app/features/org/partials/change_password.html",
+      controller: "ChangePasswordCtrl",
+      resolve: loadOrgBundle
+    })
+    .when("/profile/select-org", {
+      templateUrl: "public/app/features/org/partials/select_org.html",
+      controller: "SelectOrgCtrl",
+      resolve: loadOrgBundle
+    })
+    // ADMIN
+    .when("/admin", {
+      templateUrl: "public/app/features/admin/partials/admin_home.html",
+      controller: "AdminHomeCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/settings", {
+      templateUrl: "public/app/features/admin/partials/settings.html",
+      controller: "AdminSettingsCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/users", {
+      templateUrl: "public/app/features/admin/partials/users.html",
+      controller: "AdminListUsersCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/users/create", {
+      templateUrl: "public/app/features/admin/partials/new_user.html",
+      controller: "AdminEditUserCtrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/users/edit/:id", {
+      templateUrl: "public/app/features/admin/partials/edit_user.html",
+      controller: "AdminEditUserCtrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/orgs", {
+      templateUrl: "public/app/features/admin/partials/orgs.html",
+      controller: "AdminListOrgsCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/orgs/edit/:id", {
+      templateUrl: "public/app/features/admin/partials/edit_org.html",
+      controller: "AdminEditOrgCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    .when("/admin/stats", {
+      templateUrl: "public/app/features/admin/partials/stats.html",
+      controller: "AdminStatsCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAdminBundle
+    })
+    // LOGIN / SIGNUP
+    .when("/login", {
+      templateUrl: "public/app/partials/login.html",
+      controller: "LoginCtrl",
+      pageClass: "login-page sidemenu-hidden"
+    })
+    .when("/invite/:code", {
+      templateUrl: "public/app/partials/signup_invited.html",
+      controller: "InvitedCtrl",
+      pageClass: "sidemenu-hidden"
+    })
+    .when("/signup", {
+      templateUrl: "public/app/partials/signup_step2.html",
+      controller: "SignUpCtrl",
+      pageClass: "sidemenu-hidden"
+    })
+    .when("/user/password/send-reset-email", {
+      templateUrl: "public/app/partials/reset_password.html",
+      controller: "ResetPasswordCtrl",
+      pageClass: "sidemenu-hidden"
+    })
+    .when("/user/password/reset", {
+      templateUrl: "public/app/partials/reset_password.html",
+      controller: "ResetPasswordCtrl",
+      pageClass: "sidemenu-hidden"
+    })
+    .when("/dashboard/snapshots", {
+      templateUrl: "public/app/features/snapshot/partials/snapshots.html",
+      controller: "SnapshotsCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/plugins", {
+      templateUrl: "public/app/features/plugins/partials/plugin_list.html",
+      controller: "PluginListCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/plugins/:pluginId/edit", {
+      templateUrl: "public/app/features/plugins/partials/plugin_edit.html",
+      controller: "PluginEditCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/plugins/:pluginId/page/:slug", {
+      templateUrl: "public/app/features/plugins/partials/plugin_page.html",
+      controller: "AppPageCtrl",
+      controllerAs: "ctrl"
+    })
+    .when("/styleguide/:page?", {
+      controller: "StyleGuideCtrl",
+      controllerAs: "ctrl",
+      templateUrl: "public/app/features/styleguide/styleguide.html"
+    })
+    .when("/alerting", {
+      redirectTo: "/alerting/list"
+    })
+    .when("/alerting/list", {
+      templateUrl: "public/app/features/alerting/partials/alert_list.html",
+      controller: "AlertListCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAlertingBundle
+    })
+    .when("/alerting/notifications", {
+      templateUrl:
+        "public/app/features/alerting/partials/notifications_list.html",
+      controller: "AlertNotificationsListCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAlertingBundle
+    })
+    .when("/alerting/notification/new", {
+      templateUrl:
+        "public/app/features/alerting/partials/notification_edit.html",
+      controller: "AlertNotificationEditCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAlertingBundle
+    })
+    .when("/alerting/notification/:id/edit", {
+      templateUrl:
+        "public/app/features/alerting/partials/notification_edit.html",
+      controller: "AlertNotificationEditCtrl",
+      controllerAs: "ctrl",
+      resolve: loadAlertingBundle
+    })
+    .otherwise({
+      templateUrl: "public/app/partials/error.html",
+      controller: "ErrorCtrl"
+    });
 }
 
 coreModule.config(setupAngularRoutes);

+ 55 - 33
public/app/core/services/alert_srv.ts

@@ -1,9 +1,9 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import angular from 'angular';
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
+import angular from "angular";
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import appEvents from "app/core/app_events";
 
 export class AlertSrv {
   list: any[];
@@ -14,44 +14,65 @@ export class AlertSrv {
   }
 
   init() {
-    this.$rootScope.onAppEvent('alert-error', (e, alert) => {
-      this.set(alert[0], alert[1], 'error', 12000);
-    }, this.$rootScope);
-
-    this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
-      this.set(alert[0], alert[1], 'warning', 5000);
-    }, this.$rootScope);
-
-    this.$rootScope.onAppEvent('alert-success', (e, alert) => {
-      this.set(alert[0], alert[1], 'success', 3000);
-    }, this.$rootScope);
-
-    appEvents.on('alert-warning', options => this.set(options[0], options[1], 'warning', 5000));
-    appEvents.on('alert-success', options => this.set(options[0], options[1], 'success', 3000));
-    appEvents.on('alert-error', options => this.set(options[0], options[1], 'error', 7000));
-    appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
+    this.$rootScope.onAppEvent(
+      "alert-error",
+      (e, alert) => {
+        this.set(alert[0], alert[1], "error", 12000);
+      },
+      this.$rootScope
+    );
+
+    this.$rootScope.onAppEvent(
+      "alert-warning",
+      (e, alert) => {
+        this.set(alert[0], alert[1], "warning", 5000);
+      },
+      this.$rootScope
+    );
+
+    this.$rootScope.onAppEvent(
+      "alert-success",
+      (e, alert) => {
+        this.set(alert[0], alert[1], "success", 3000);
+      },
+      this.$rootScope
+    );
+
+    appEvents.on("alert-warning", options =>
+      this.set(options[0], options[1], "warning", 5000)
+    );
+    appEvents.on("alert-success", options =>
+      this.set(options[0], options[1], "success", 3000)
+    );
+    appEvents.on("alert-error", options =>
+      this.set(options[0], options[1], "error", 7000)
+    );
+    appEvents.on("confirm-modal", this.showConfirmModal.bind(this));
   }
 
   getIconForSeverity(severity) {
     switch (severity) {
-      case 'success': return 'fa fa-check';
-      case 'error': return 'fa fa-exclamation-triangle';
-      default: return 'fa fa-exclamation';
+      case "success":
+        return "fa fa-check";
+      case "error":
+        return "fa fa-exclamation-triangle";
+      default:
+        return "fa fa-exclamation";
     }
   }
 
   set(title, text, severity, timeout) {
     if (_.isObject(text)) {
-      console.log('alert error', text);
+      console.log("alert error", text);
       if (text.statusText) {
         text = `HTTP Error (${text.status}) ${text.statusText}`;
       }
     }
 
     var newAlert = {
-      title: title || '',
-      text: text || '',
-      severity: severity || 'info',
+      title: title || "",
+      text: text || "",
+      severity: severity || "info",
       icon: this.getIconForSeverity(severity)
     };
 
@@ -73,7 +94,7 @@ export class AlertSrv {
       this.$rootScope.$digest();
     }
 
-    return(newAlert);
+    return newAlert;
   }
 
   clear(alert) {
@@ -93,7 +114,8 @@ export class AlertSrv {
     };
 
     scope.updateConfirmText = function(value) {
-      scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase();
+      scope.confirmTextValid =
+        payload.confirmText.toLowerCase() === value.toLowerCase();
     };
 
     scope.title = payload.title;
@@ -110,18 +132,18 @@ export class AlertSrv {
     scope.confirmTextValid = scope.confirmText ? false : true;
 
     var confirmModal = this.$modal({
-      template: 'public/app/partials/confirm_modal.html',
+      template: "public/app/partials/confirm_modal.html",
       persist: false,
-      modalClass: 'confirm-modal',
+      modalClass: "confirm-modal",
       show: false,
       scope: scope,
       keyboard: false
     });
 
     confirmModal.then(function(modalEl) {
-      modalEl.modal('show');
+      modalEl.modal("show");
     });
   }
 }
 
-coreModule.service('alertSrv', AlertSrv);
+coreModule.service("alertSrv", AlertSrv);

+ 16 - 14
public/app/core/services/analytics.ts

@@ -1,26 +1,29 @@
-import $ from 'jquery';
-import coreModule from 'app/core/core_module';
-import config from 'app/core/config';
+import $ from "jquery";
+import coreModule from "app/core/core_module";
+import config from "app/core/config";
 
 export class Analytics {
-
   /** @ngInject */
-  constructor(private $rootScope, private $location) {
-  }
+  constructor(private $rootScope, private $location) {}
 
   gaInit() {
-    $.getScript('https://www.google-analytics.com/analytics.js'); // jQuery shortcut
-    var ga = (<any>window).ga = (<any>window).ga || function () { (ga.q = ga.q || []).push(arguments); }; ga.l = +new Date;
-    ga('create', (<any>config).googleAnalyticsId, 'auto');
+    $.getScript("https://www.google-analytics.com/analytics.js"); // jQuery shortcut
+    var ga = ((<any>window).ga =
+      (<any>window).ga ||
+      function() {
+        (ga.q = ga.q || []).push(arguments);
+      });
+    ga.l = +new Date();
+    ga("create", (<any>config).googleAnalyticsId, "auto");
     return ga;
   }
 
   init() {
-    this.$rootScope.$on('$viewContentLoaded', () => {
+    this.$rootScope.$on("$viewContentLoaded", () => {
       var track = { page: this.$location.url() };
       var ga = (<any>window).ga || this.gaInit();
-      ga('set', track);
-      ga('send', 'pageview');
+      ga("set", track);
+      ga("send", "pageview");
     });
   }
 }
@@ -32,5 +35,4 @@ function startAnalytics(googleAnalyticsSrv) {
   }
 }
 
-coreModule.service('googleAnalyticsSrv', Analytics).run(startAnalytics);
-
+coreModule.service("googleAnalyticsSrv", Analytics).run(startAnalytics);

+ 138 - 115
public/app/core/services/backend_srv.ts

@@ -1,9 +1,9 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
-import { DashboardModel } from 'app/features/dashboard/dashboard_model';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import appEvents from "app/core/app_events";
+import { DashboardModel } from "app/features/dashboard/dashboard_model";
 
 export class BackendSrv {
   private inFlightRequests = {};
@@ -11,27 +11,32 @@ export class BackendSrv {
   private noBackendCache: boolean;
 
   /** @ngInject */
-  constructor(private $http, private alertSrv, private $q, private $timeout, private contextSrv) {
-  }
+  constructor(
+    private $http,
+    private alertSrv,
+    private $q,
+    private $timeout,
+    private contextSrv
+  ) {}
 
   get(url, params?) {
-    return this.request({ method: 'GET', url: url, params: params });
+    return this.request({ method: "GET", url: url, params: params });
   }
 
   delete(url) {
-    return this.request({ method: 'DELETE', url: url });
+    return this.request({ method: "DELETE", url: url });
   }
 
   post(url, data) {
-    return this.request({ method: 'POST', url: url, data: data });
+    return this.request({ method: "POST", url: url, data: data });
   }
 
   patch(url, data) {
-    return this.request({ method: 'PATCH', url: url, data: data });
+    return this.request({ method: "PATCH", url: url, data: data });
   }
 
   put(url, data) {
-    return this.request({ method: 'PUT', url: url, data: data });
+    return this.request({ method: "PUT", url: url, data: data });
   }
 
   withNoBackendCache(callback) {
@@ -46,7 +51,7 @@ export class BackendSrv {
       return;
     }
 
-    var data = err.data || { message: 'Unexpected error' };
+    var data = err.data || { message: "Unexpected error" };
     if (_.isString(data)) {
       data = { message: data };
     }
@@ -56,7 +61,7 @@ export class BackendSrv {
       throw data;
     }
 
-    data.severity = 'error';
+    data.severity = "error";
 
     if (err.status < 500) {
       data.severity = "warning";
@@ -83,7 +88,7 @@ export class BackendSrv {
     if (requestIsLocal) {
       if (this.contextSrv.user && this.contextSrv.user.orgId) {
         options.headers = options.headers || {};
-        options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
+        options.headers["X-Grafana-Org-Id"] = this.contextSrv.user.orgId;
       }
 
       if (options.url.indexOf("/") === 0) {
@@ -91,27 +96,34 @@ export class BackendSrv {
       }
     }
 
-    return this.$http(options).then(results => {
-      if (options.method !== 'GET') {
-        if (results && results.data.message) {
-          if (options.showSuccessAlert !== false) {
-            this.alertSrv.set(results.data.message, '', 'success', 3000);
+    return this.$http(options).then(
+      results => {
+        if (options.method !== "GET") {
+          if (results && results.data.message) {
+            if (options.showSuccessAlert !== false) {
+              this.alertSrv.set(results.data.message, "", "success", 3000);
+            }
           }
         }
-      }
-      return results.data;
-    }, err => {
-      // handle unauthorized
-      if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
-        return this.loginPing().then(() => {
-          options.retry = 1;
-          return this.request(options);
-        });
-      }
+        return results.data;
+      },
+      err => {
+        // handle unauthorized
+        if (
+          err.status === 401 &&
+          this.contextSrv.user.isSignedIn &&
+          firstAttempt
+        ) {
+          return this.loginPing().then(() => {
+            options.retry = 1;
+            return this.request(options);
+          });
+        }
 
-      this.$timeout(this.requestErrorHandler.bind(this, err), 50);
-      throw err;
-    });
+        this.$timeout(this.requestErrorHandler.bind(this, err), 50);
+        throw err;
+      }
+    );
   }
 
   addCanceler(requestId, canceler) {
@@ -151,7 +163,7 @@ export class BackendSrv {
     if (requestIsLocal) {
       if (this.contextSrv.user && this.contextSrv.user.orgId) {
         options.headers = options.headers || {};
-        options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
+        options.headers["X-Grafana-Org-Id"] = this.contextSrv.user.orgId;
       }
 
       if (options.url.indexOf("/") === 0) {
@@ -159,78 +171,80 @@ export class BackendSrv {
       }
 
       if (options.headers && options.headers.Authorization) {
-        options.headers['X-DS-Authorization'] = options.headers.Authorization;
+        options.headers["X-DS-Authorization"] = options.headers.Authorization;
         delete options.headers.Authorization;
       }
 
       if (this.noBackendCache) {
-        options.headers['X-Grafana-NoCache'] = 'true';
+        options.headers["X-Grafana-NoCache"] = "true";
       }
     }
 
-    return this.$http(options).then(response => {
-      appEvents.emit('ds-request-response', response);
-      return response;
-    }).catch(err => {
-      if (err.status === this.HTTP_REQUEST_CANCELLED) {
-        throw {err, cancelled: true};
-      }
-
-      // handle unauthorized for backend requests
-      if (requestIsLocal && firstAttempt && err.status === 401) {
-        return this.loginPing().then(() => {
-          options.retry = 1;
-          if (canceler) {
-            canceler.resolve();
-          }
-          return this.datasourceRequest(options);
-        });
-      }
+    return this.$http(options)
+      .then(response => {
+        appEvents.emit("ds-request-response", response);
+        return response;
+      })
+      .catch(err => {
+        if (err.status === this.HTTP_REQUEST_CANCELLED) {
+          throw { err, cancelled: true };
+        }
 
-      // populate error obj on Internal Error
-      if (_.isString(err.data) && err.status === 500) {
-        err.data = {
-          error: err.statusText,
-          response: err.data,
-        };
-      }
+        // handle unauthorized for backend requests
+        if (requestIsLocal && firstAttempt && err.status === 401) {
+          return this.loginPing().then(() => {
+            options.retry = 1;
+            if (canceler) {
+              canceler.resolve();
+            }
+            return this.datasourceRequest(options);
+          });
+        }
 
-      // for Prometheus
-      if (err.data && !err.data.message && _.isString(err.data.error)) {
-        err.data.message = err.data.error;
-      }
+        // populate error obj on Internal Error
+        if (_.isString(err.data) && err.status === 500) {
+          err.data = {
+            error: err.statusText,
+            response: err.data
+          };
+        }
 
-      appEvents.emit('ds-request-error', err);
-      throw err;
+        // for Prometheus
+        if (err.data && !err.data.message && _.isString(err.data.error)) {
+          err.data.message = err.data.error;
+        }
 
-    }).finally(() => {
-      // clean up
-      if (options.requestId) {
-        this.inFlightRequests[options.requestId].shift();
-      }
-    });
+        appEvents.emit("ds-request-error", err);
+        throw err;
+      })
+      .finally(() => {
+        // clean up
+        if (options.requestId) {
+          this.inFlightRequests[options.requestId].shift();
+        }
+      });
   }
 
   loginPing() {
-    return this.request({url: '/api/login/ping', method: 'GET', retry: 1 });
+    return this.request({ url: "/api/login/ping", method: "GET", retry: 1 });
   }
 
   search(query) {
-    return this.get('/api/search', query);
+    return this.get("/api/search", query);
   }
 
   getDashboard(type, slug) {
-    return this.get('/api/dashboards/' + type + '/' + slug);
+    return this.get("/api/dashboards/" + type + "/" + slug);
   }
 
   saveDashboard(dash, options) {
-    options = (options || {});
+    options = options || {};
 
-    return this.post('/api/dashboards/db/', {
+    return this.post("/api/dashboards/db/", {
       dashboard: dash,
       folderId: dash.folderId,
       overwrite: options.overwrite === true,
-      message: options.message || '',
+      message: options.message || ""
     });
   }
 
@@ -242,24 +256,27 @@ export class BackendSrv {
       panels: []
     };
 
-    return this.post('/api/dashboards/db/', {dashboard: dash, isFolder: true, overwrite: false})
-    .then(res => {
-      return this.getDashboard('db', res.slug);
+    return this.post("/api/dashboards/db/", {
+      dashboard: dash,
+      isFolder: true,
+      overwrite: false
+    }).then(res => {
+      return this.getDashboard("db", res.slug);
     });
   }
 
   deleteDashboard(slug) {
     let deferred = this.$q.defer();
 
-    this.getDashboard('db', slug)
-      .then(fullDash => {
-        this.delete(`/api/dashboards/db/${slug}`)
-          .then(() => {
-            deferred.resolve(fullDash);
-          }).catch(err => {
-            deferred.reject(err);
-          });
-      });
+    this.getDashboard("db", slug).then(fullDash => {
+      this.delete(`/api/dashboards/db/${slug}`)
+        .then(() => {
+          deferred.resolve(fullDash);
+        })
+        .catch(err => {
+          deferred.reject(err);
+        });
+    });
 
     return deferred.promise;
   }
@@ -278,28 +295,31 @@ export class BackendSrv {
     const tasks = [];
 
     for (let slug of dashboardSlugs) {
-      tasks.push(this.createTask(this.moveDashboard.bind(this), true, slug, toFolder));
+      tasks.push(
+        this.createTask(this.moveDashboard.bind(this), true, slug, toFolder)
+      );
     }
 
-    return this.executeInOrder(tasks, [])
-      .then(result => {
-        return {
-          totalCount: result.length,
-          successCount: _.filter(result, { succeeded: true }).length,
-          alreadyInFolderCount: _.filter(result, { alreadyInFolder: true }).length
-        };
-      });
+    return this.executeInOrder(tasks, []).then(result => {
+      return {
+        totalCount: result.length,
+        successCount: _.filter(result, { succeeded: true }).length,
+        alreadyInFolderCount: _.filter(result, { alreadyInFolder: true }).length
+      };
+    });
   }
 
   private moveDashboard(slug, toFolder) {
     let deferred = this.$q.defer();
 
-    this.getDashboard('db', slug).then(fullDash => {
+    this.getDashboard("db", slug).then(fullDash => {
       const model = new DashboardModel(fullDash.dashboard, fullDash.meta);
 
-      if ((!fullDash.meta.folderId && toFolder.id === 0) ||
-        fullDash.meta.folderId === toFolder.id) {
-        deferred.resolve({alreadyInFolder: true});
+      if (
+        (!fullDash.meta.folderId && toFolder.id === 0) ||
+        fullDash.meta.folderId === toFolder.id
+      ) {
+        deferred.resolve({ alreadyInFolder: true });
         return;
       }
 
@@ -310,19 +330,21 @@ export class BackendSrv {
 
       this.saveDashboard(clone, {})
         .then(() => {
-          deferred.resolve({succeeded: true});
-        }).catch(err => {
+          deferred.resolve({ succeeded: true });
+        })
+        .catch(err => {
           if (err.data && err.data.status === "plugin-dashboard") {
             err.isHandled = true;
 
-            this.saveDashboard(clone, {overwrite: true})
+            this.saveDashboard(clone, { overwrite: true })
               .then(() => {
-                deferred.resolve({succeeded: true});
-              }).catch(err => {
-                deferred.resolve({succeeded: false});
+                deferred.resolve({ succeeded: true });
+              })
+              .catch(err => {
+                deferred.resolve({ succeeded: false });
               });
           } else {
-            deferred.resolve({succeeded: false});
+            deferred.resolve({ succeeded: false });
           }
         });
     });
@@ -331,11 +353,13 @@ export class BackendSrv {
   }
 
   private createTask(fn, ignoreRejections, ...args: any[]) {
-    return (result) => {
-      return fn.apply(null, args)
+    return result => {
+      return fn
+        .apply(null, args)
         .then(res => {
           return Array.prototype.concat(result, [res]);
-        }).catch(err => {
+        })
+        .catch(err => {
           if (ignoreRejections) {
             return result;
           }
@@ -350,5 +374,4 @@ export class BackendSrv {
   }
 }
 
-
-coreModule.service('backendSrv', BackendSrv);
+coreModule.service("backendSrv", BackendSrv);

+ 14 - 11
public/app/core/services/context_srv.ts

@@ -1,7 +1,7 @@
-import config from 'app/core/config';
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import store from 'app/core/store';
+import config from "app/core/config";
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import store from "app/core/store";
 
 export class User {
   isGrafanaAdmin: any;
@@ -30,20 +30,20 @@ export class ContextSrv {
   sidemenuSmallBreakpoint = false;
 
   constructor() {
-    this.sidemenu = store.getBool('grafana.sidemenu', true);
+    this.sidemenu = store.getBool("grafana.sidemenu", true);
 
     if (!config.buildInfo) {
       config.buildInfo = {};
     }
     if (!config.bootData) {
-      config.bootData = {user: {}, settings: {}};
+      config.bootData = { user: {}, settings: {} };
     }
 
     this.version = config.buildInfo.version;
     this.user = new User();
     this.isSignedIn = this.user.isSignedIn;
     this.isGrafanaAdmin = this.user.isGrafanaAdmin;
-    this.isEditor = this.hasRole('Editor') || this.hasRole('Admin');
+    this.isEditor = this.hasRole("Editor") || this.hasRole("Admin");
   }
 
   hasRole(role) {
@@ -51,18 +51,21 @@ export class ContextSrv {
   }
 
   isGrafanaVisible() {
-    return !!(document.visibilityState === undefined || document.visibilityState === 'visible');
+    return !!(
+      document.visibilityState === undefined ||
+      document.visibilityState === "visible"
+    );
   }
 
   toggleSideMenu() {
     this.sidemenu = !this.sidemenu;
-    store.set('grafana.sidemenu', this.sidemenu);
+    store.set("grafana.sidemenu", this.sidemenu);
   }
 }
 
 var contextSrv = new ContextSrv();
-export {contextSrv};
+export { contextSrv };
 
-coreModule.factory('contextSrv', function() {
+coreModule.factory("contextSrv", function() {
   return contextSrv;
 });

+ 27 - 21
public/app/core/services/dynamic_directive_srv.ts

@@ -1,10 +1,9 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import angular from 'angular';
-import coreModule from '../core_module';
+import angular from "angular";
+import coreModule from "../core_module";
 
 class DynamicDirectiveSrv {
-
   /** @ngInject */
   constructor(private $compile, private $rootScope) {}
 
@@ -17,27 +16,36 @@ class DynamicDirectiveSrv {
   }
 
   link(scope, elem, attrs, options) {
-    options.directive(scope).then(directiveInfo => {
-      if (!directiveInfo || !directiveInfo.fn) {
-        elem.empty();
-        return;
-      }
+    options
+      .directive(scope)
+      .then(directiveInfo => {
+        if (!directiveInfo || !directiveInfo.fn) {
+          elem.empty();
+          return;
+        }
 
-      if (!directiveInfo.fn.registered) {
-        coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
-        directiveInfo.fn.registered = true;
-      }
+        if (!directiveInfo.fn.registered) {
+          coreModule.directive(
+            attrs.$normalize(directiveInfo.name),
+            directiveInfo.fn
+          );
+          directiveInfo.fn.registered = true;
+        }
 
-      this.addDirective(elem, directiveInfo.name, scope);
-    }).catch(err => {
-      console.log('Plugin load:', err);
-      this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]);
-    });
+        this.addDirective(elem, directiveInfo.name, scope);
+      })
+      .catch(err => {
+        console.log("Plugin load:", err);
+        this.$rootScope.appEvent("alert-error", [
+          "Plugin error",
+          err.toString()
+        ]);
+      });
   }
 
   create(options) {
     let directiveDef = {
-      restrict: 'E',
+      restrict: "E",
       scope: options.scope,
       link: (scope, elem, attrs) => {
         if (options.watchPath) {
@@ -59,6 +67,4 @@ class DynamicDirectiveSrv {
   }
 }
 
-coreModule.service('dynamicDirectiveSrv', DynamicDirectiveSrv);
-
-
+coreModule.service("dynamicDirectiveSrv", DynamicDirectiveSrv);

+ 14 - 12
public/app/core/services/global_event_srv.ts

@@ -1,6 +1,6 @@
-import coreModule from 'app/core/core_module';
-import config from 'app/core/config';
-import appEvents from 'app/core/app_events';
+import coreModule from "app/core/core_module";
+import config from "app/core/config";
+import appEvents from "app/core/app_events";
 
 // This service is for registering global events.
 // Good for communication react > angular and vice verse
@@ -13,25 +13,27 @@ export class GlobalEventSrv {
   }
 
   // Angular's $location does not like <base href...> and absolute urls
-  stripBaseFromUrl (url = '') {
+  stripBaseFromUrl(url = "") {
     const appSubUrl = this.appSubUrl;
-    const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
-    const urlWithoutBase = url.length > 0 && url.indexOf(appSubUrl) === 0 ?
-      url.slice(appSubUrl.length - stripExtraChars)
-      : url;
+    const stripExtraChars = appSubUrl.endsWith("/") ? 1 : 0;
+    const urlWithoutBase =
+      url.length > 0 && url.indexOf(appSubUrl) === 0
+        ? url.slice(appSubUrl.length - stripExtraChars)
+        : url;
 
     return urlWithoutBase;
   }
 
   init() {
-    appEvents.on('location-change', payload => {
+    appEvents.on("location-change", payload => {
       const urlWithoutBase = this.stripBaseFromUrl(payload.href);
 
-      this.$timeout(() => { // A hack to use timeout when we're changing things (in this case the url) from outside of Angular.
-          this.$location.url(urlWithoutBase);
+      this.$timeout(() => {
+        // A hack to use timeout when we're changing things (in this case the url) from outside of Angular.
+        this.$location.url(urlWithoutBase);
       });
     });
   }
 }
 
-coreModule.service('globalEventSrv', GlobalEventSrv);
+coreModule.service("globalEventSrv", GlobalEventSrv);

+ 4 - 4
public/app/core/services/impression_srv.ts

@@ -1,6 +1,6 @@
-import store from 'app/core/store';
-import _ from 'lodash';
-import config from 'app/core/config';
+import store from "app/core/store";
+import _ from "lodash";
+import config from "app/core/config";
 
 export class ImpressionSrv {
   constructor() {}
@@ -15,7 +15,7 @@ export class ImpressionSrv {
       }
     }
 
-    impressions = impressions.filter((imp) => {
+    impressions = impressions.filter(imp => {
       return dashboardId !== imp;
     });
 

+ 66 - 65
public/app/core/services/keybindingSrv.ts

@@ -1,21 +1,18 @@
-import $ from 'jquery';
-import _ from 'lodash';
+import $ from "jquery";
+import _ from "lodash";
 
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
+import coreModule from "app/core/core_module";
+import appEvents from "app/core/app_events";
 
-import Mousetrap from 'mousetrap';
+import Mousetrap from "mousetrap";
 
 export class KeybindingSrv {
   helpModal: boolean;
 
   /** @ngInject */
-  constructor(
-    private $rootScope,
-    private $location) {
-
+  constructor(private $rootScope, private $location) {
     // clear out all shortcuts on route change
-    $rootScope.$on('$routeChangeSuccess', () => {
+    $rootScope.$on("$routeChangeSuccess", () => {
       Mousetrap.reset();
       // rebind global shortcuts
       this.setupGlobal();
@@ -25,26 +22,26 @@ export class KeybindingSrv {
   }
 
   setupGlobal() {
-    this.bind(['?', 'h'], this.showHelpModal);
+    this.bind(["?", "h"], this.showHelpModal);
     this.bind("g h", this.goToHome);
     this.bind("g a", this.openAlerting);
     this.bind("g p", this.goToProfile);
     this.bind("s s", this.openSearchStarred);
-    this.bind('s o', this.openSearch);
-    this.bind('s t', this.openSearchTags);
-    this.bind('f', this.openSearch);
+    this.bind("s o", this.openSearch);
+    this.bind("s t", this.openSearchTags);
+    this.bind("f", this.openSearch);
   }
 
   openSearchStarred() {
-    appEvents.emit('show-dash-search', {starred: true});
+    appEvents.emit("show-dash-search", { starred: true });
   }
 
   openSearchTags() {
-    appEvents.emit('show-dash-search', {tagsMode: true});
+    appEvents.emit("show-dash-search", { tagsMode: true });
   }
 
   openSearch() {
-    appEvents.emit('show-dash-search');
+    appEvents.emit("show-dash-search");
   }
 
   openAlerting() {
@@ -60,54 +57,58 @@ export class KeybindingSrv {
   }
 
   showHelpModal() {
-    appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
+    appEvents.emit("show-modal", { templateHtml: "<help-modal></help-modal>" });
   }
 
   bind(keyArg, fn) {
-    Mousetrap.bind(keyArg, evt => {
-      evt.preventDefault();
-      evt.stopPropagation();
-      evt.returnValue = false;
-      return this.$rootScope.$apply(fn.bind(this));
-    }, 'keydown');
+    Mousetrap.bind(
+      keyArg,
+      evt => {
+        evt.preventDefault();
+        evt.stopPropagation();
+        evt.returnValue = false;
+        return this.$rootScope.$apply(fn.bind(this));
+      },
+      "keydown"
+    );
   }
 
   showDashEditView() {
-    var search = _.extend(this.$location.search(), {editview: 'settings'});
+    var search = _.extend(this.$location.search(), { editview: "settings" });
     this.$location.search(search);
   }
 
   setupDashboardBindings(scope, dashboard) {
-    this.bind('mod+o', () => {
+    this.bind("mod+o", () => {
       dashboard.graphTooltip = (dashboard.graphTooltip + 1) % 3;
-      appEvents.emit('graph-hover-clear');
-      this.$rootScope.$broadcast('refresh');
+      appEvents.emit("graph-hover-clear");
+      this.$rootScope.$broadcast("refresh");
     });
 
-    this.bind('mod+s', e => {
-      scope.appEvent('save-dashboard');
+    this.bind("mod+s", e => {
+      scope.appEvent("save-dashboard");
     });
 
-    this.bind('t z', () => {
-      scope.appEvent('zoom-out', 2);
+    this.bind("t z", () => {
+      scope.appEvent("zoom-out", 2);
     });
 
-    this.bind('ctrl+z', () => {
-      scope.appEvent('zoom-out', 2);
+    this.bind("ctrl+z", () => {
+      scope.appEvent("zoom-out", 2);
     });
 
-    this.bind('t left', () => {
-      scope.appEvent('shift-time-backward');
+    this.bind("t left", () => {
+      scope.appEvent("shift-time-backward");
     });
 
-    this.bind('t right', () => {
-      scope.appEvent('shift-time-forward');
+    this.bind("t right", () => {
+      scope.appEvent("shift-time-forward");
     });
 
     // edit panel
-    this.bind('e', () => {
+    this.bind("e", () => {
       if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
-        this.$rootScope.appEvent('panel-change-view', {
+        this.$rootScope.appEvent("panel-change-view", {
           fullscreen: true,
           edit: true,
           panelId: dashboard.meta.focusPanelId,
@@ -117,19 +118,19 @@ export class KeybindingSrv {
     });
 
     // view panel
-    this.bind('v', () => {
+    this.bind("v", () => {
       if (dashboard.meta.focusPanelId) {
-        this.$rootScope.appEvent('panel-change-view', {
+        this.$rootScope.appEvent("panel-change-view", {
           fullscreen: true,
           edit: null,
           panelId: dashboard.meta.focusPanelId,
-          toggle: true,
+          toggle: true
         });
       }
     });
 
     // delete panel
-    this.bind('p r', () => {
+    this.bind("p r", () => {
       if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
         var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
         panelInfo.row.removePanel(panelInfo.panel);
@@ -138,22 +139,22 @@ export class KeybindingSrv {
     });
 
     // share panel
-    this.bind('p s', () => {
+    this.bind("p s", () => {
       if (dashboard.meta.focusPanelId) {
-        var shareScope =  scope.$new();
+        var shareScope = scope.$new();
         var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
         shareScope.panel = panelInfo.panel;
         shareScope.dashboard = dashboard;
 
-        appEvents.emit('show-modal', {
-          src: 'public/app/features/dashboard/partials/shareModal.html',
+        appEvents.emit("show-modal", {
+          src: "public/app/features/dashboard/partials/shareModal.html",
           scope: shareScope
         });
       }
     });
 
     // delete row
-    this.bind('r r', () => {
+    this.bind("r r", () => {
       if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
         var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
         dashboard.removeRow(panelInfo.row);
@@ -162,7 +163,7 @@ export class KeybindingSrv {
     });
 
     // collapse row
-    this.bind('r c', () => {
+    this.bind("r c", () => {
       if (dashboard.meta.focusPanelId) {
         var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
         panelInfo.row.toggleCollapse();
@@ -171,47 +172,47 @@ export class KeybindingSrv {
     });
 
     // collapse all rows
-    this.bind('d shift+c', () => {
+    this.bind("d shift+c", () => {
       for (let row of dashboard.rows) {
         row.collapse = true;
       }
     });
 
     // expand all rows
-    this.bind('d shift+e', () => {
+    this.bind("d shift+e", () => {
       for (let row of dashboard.rows) {
         row.collapse = false;
       }
     });
 
-    this.bind('d n', e => {
+    this.bind("d n", e => {
       this.$location.url("/dashboard/new");
     });
 
-    this.bind('d r', () => {
-      this.$rootScope.$broadcast('refresh');
+    this.bind("d r", () => {
+      this.$rootScope.$broadcast("refresh");
     });
 
-    this.bind('d s', () => {
+    this.bind("d s", () => {
       this.showDashEditView();
     });
 
-    this.bind('d k', () => {
-      appEvents.emit('toggle-kiosk-mode');
+    this.bind("d k", () => {
+      appEvents.emit("toggle-kiosk-mode");
     });
 
-    this.bind('d v', () => {
-      appEvents.emit('toggle-view-mode');
+    this.bind("d v", () => {
+      appEvents.emit("toggle-view-mode");
     });
 
-    this.bind('esc', () => {
-      var popups = $('.popover.in');
+    this.bind("esc", () => {
+      var popups = $(".popover.in");
       if (popups.length > 0) {
         return;
       }
 
-      scope.appEvent('hide-modal');
-      scope.appEvent('panel-change-view', {fullscreen: false, edit: false});
+      scope.appEvent("hide-modal");
+      scope.appEvent("panel-change-view", { fullscreen: false, edit: false });
 
       // close settings view
       var search = this.$location.search();
@@ -223,4 +224,4 @@ export class KeybindingSrv {
   }
 }
 
-coreModule.service('keybindingSrv', KeybindingSrv);
+coreModule.service("keybindingSrv", KeybindingSrv);

+ 60 - 40
public/app/core/services/ng_react.ts

@@ -2,7 +2,6 @@
 // This is using ng-react with this PR applied https://github.com/ngReact/ngReact/pull/199
 //
 
-
 // # ngReact
 // ### Use React Components inside of your Angular applications
 //
@@ -10,9 +9,9 @@
 // - reactComponent (generic directive for delegating off to React Components)
 // - reactDirective (factory for creating specific directives that correspond to reactComponent directives)
 
-import React from 'react';
-import ReactDOM from 'react-dom';
-import angular from 'angular';
+import React from "react";
+import ReactDOM from "react-dom";
+import angular from "angular";
 
 // get a react component from name (components can be an angular injectable e.g. value, factory or
 // available on window
@@ -24,7 +23,7 @@ function getReactComponent(name, $injector) {
 
   // a React component name must be specified
   if (!name) {
-    throw new Error('ReactComponent name attribute must be specified');
+    throw new Error("ReactComponent name attribute must be specified");
   }
 
   // ensure the specified React component is accessible, and fail fast if it's not
@@ -35,14 +34,14 @@ function getReactComponent(name, $injector) {
 
   if (!reactComponent) {
     try {
-      reactComponent = name.split('.').reduce(function(current, namePart) {
+      reactComponent = name.split(".").reduce(function(current, namePart) {
         return current[namePart];
       }, window);
     } catch (e) {}
   }
 
   if (!reactComponent) {
-    throw Error('Cannot find react component ' + name);
+    throw Error("Cannot find react component " + name);
   }
 
   return reactComponent;
@@ -56,7 +55,7 @@ function applied(fn, scope) {
   var wrapped: any = function() {
     var args = arguments;
     var phase = scope.$root.$$phase;
-    if (phase === '$apply' || phase === '$digest') {
+    if (phase === "$apply" || phase === "$digest") {
       return fn.apply(null, args);
     } else {
       return scope.$apply(function() {
@@ -85,24 +84,27 @@ function applyFunctions(obj, scope, propsConfig?) {
     var value = obj[key];
     var config = (propsConfig || {})[key] || {};
     /**
-       * wrap functions in a function that ensures they are scope.$applied
-       * ensures that when function is called from a React component
-       * the Angular digest cycle is run
-       */
-    prev[key] = angular.isFunction(value) && config.wrapApply !== false ? applied(value, scope) : value;
+     * wrap functions in a function that ensures they are scope.$applied
+     * ensures that when function is called from a React component
+     * the Angular digest cycle is run
+     */
+    prev[key] =
+      angular.isFunction(value) && config.wrapApply !== false
+        ? applied(value, scope)
+        : value;
 
     return prev;
   }, {});
 }
 
 /**
-   *
-   * @param watchDepth (value of HTML watch-depth attribute)
-   * @param scope (angular scope)
-   *
-   * Uses the watchDepth attribute to determine how to watch props on scope.
-   * If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value
-   */
+ *
+ * @param watchDepth (value of HTML watch-depth attribute)
+ * @param scope (angular scope)
+ *
+ * Uses the watchDepth attribute to determine how to watch props on scope.
+ * If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value
+ */
 function watchProps(watchDepth, scope, watchExpressions, listener) {
   var supportsWatchCollection = angular.isFunction(scope.$watchCollection);
   var supportsWatchGroup = angular.isFunction(scope.$watchGroup);
@@ -113,18 +115,18 @@ function watchProps(watchDepth, scope, watchExpressions, listener) {
     var actualExpr = getPropExpression(expr);
     var exprWatchDepth = getPropWatchDepth(watchDepth, expr);
 
-    if (exprWatchDepth === 'collection' && supportsWatchCollection) {
+    if (exprWatchDepth === "collection" && supportsWatchCollection) {
       scope.$watchCollection(actualExpr, listener);
-    } else if (exprWatchDepth === 'reference' && supportsWatchGroup) {
+    } else if (exprWatchDepth === "reference" && supportsWatchGroup) {
       watchGroupExpressions.push(actualExpr);
-    } else if (exprWatchDepth === 'one-time') {
+    } else if (exprWatchDepth === "one-time") {
       //do nothing because we handle our one time bindings after this
     } else {
-      scope.$watch(actualExpr, listener, exprWatchDepth !== 'reference');
+      scope.$watch(actualExpr, listener, exprWatchDepth !== "reference");
     }
   });
 
-  if (watchDepth === 'one-time') {
+  if (watchDepth === "one-time") {
     listener();
   }
 
@@ -165,7 +167,8 @@ function findAttribute(attrs, propName) {
 
 // get watch depth of prop (string or array)
 function getPropWatchDepth(defaultWatch, prop) {
-  var customWatchDepth = Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
+  var customWatchDepth =
+    Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
   return customWatchDepth || defaultWatch;
 }
 
@@ -189,7 +192,7 @@ function getPropWatchDepth(defaultWatch, prop) {
 //
 var reactComponent = function($injector) {
   return {
-    restrict: 'E',
+    restrict: "E",
     replace: true,
     link: function(scope, elem, attrs) {
       var reactComponent = getReactComponent(attrs.name, $injector);
@@ -202,19 +205,24 @@ var reactComponent = function($injector) {
       };
 
       // If there are props, re-render when they change
-      attrs.props ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent) : renderMyComponent();
+      attrs.props
+        ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent)
+        : renderMyComponent();
 
       // cleanup when scope is destroyed
-      scope.$on('$destroy', function() {
+      scope.$on("$destroy", function() {
         if (!attrs.onScopeDestroy) {
           ReactDOM.unmountComponentAtNode(elem[0]);
         } else {
           scope.$eval(attrs.onScopeDestroy, {
-            unmountComponent: ReactDOM.unmountComponentAtNode.bind(this, elem[0]),
+            unmountComponent: ReactDOM.unmountComponentAtNode.bind(
+              this,
+              elem[0]
+            )
           });
         }
       });
-    },
+    }
   };
 };
 
@@ -247,7 +255,7 @@ var reactComponent = function($injector) {
 var reactDirective = function($injector) {
   return function(reactComponentName, props, conf, injectableProps) {
     var directive = {
-      restrict: 'E',
+      restrict: "E",
       replace: true,
       link: function(scope, elem, attrs) {
         var reactComponent = getReactComponent(reactComponentName, $injector);
@@ -274,28 +282,40 @@ var reactDirective = function($injector) {
         // watch each property name and trigger an update whenever something changes,
         // to update scope.props with new values
         var propExpressions = props.map(function(prop) {
-          return Array.isArray(prop) ? [attrs[getPropName(prop)], getPropConfig(prop)] : attrs[prop];
+          return Array.isArray(prop)
+            ? [attrs[getPropName(prop)], getPropConfig(prop)]
+            : attrs[prop];
         });
 
         // If we don't have any props, then our watch statement won't fire.
-        props.length ? watchProps(attrs.watchDepth, scope, propExpressions, renderMyComponent) : renderMyComponent();
+        props.length
+          ? watchProps(
+              attrs.watchDepth,
+              scope,
+              propExpressions,
+              renderMyComponent
+            )
+          : renderMyComponent();
 
         // cleanup when scope is destroyed
-        scope.$on('$destroy', function() {
+        scope.$on("$destroy", function() {
           if (!attrs.onScopeDestroy) {
             ReactDOM.unmountComponentAtNode(elem[0]);
           } else {
             scope.$eval(attrs.onScopeDestroy, {
-              unmountComponent: ReactDOM.unmountComponentAtNode.bind(this, elem[0]),
+              unmountComponent: ReactDOM.unmountComponentAtNode.bind(
+                this,
+                elem[0]
+              )
             });
           }
         });
-      },
+      }
     };
     return angular.extend(directive, conf);
   };
 };
 
-let ngModule = angular.module('react', []);
-ngModule.directive('reactComponent', ['$injector', reactComponent]);
-ngModule.factory('reactDirective', ['$injector', reactDirective]);
+let ngModule = angular.module("react", []);
+ngModule.directive("reactComponent", ["$injector", reactComponent]);
+ngModule.factory("reactDirective", ["$injector", reactDirective]);

+ 8 - 9
public/app/core/services/popover_srv.ts

@@ -1,8 +1,8 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import Drop from 'tether-drop';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import Drop from "tether-drop";
 
 /** @ngInject **/
 function popoverSrv($compile, $rootScope, $timeout) {
@@ -43,7 +43,7 @@ function popoverSrv($compile, $rootScope, $timeout) {
       drop.close();
     };
 
-    var contentElement = document.createElement('div');
+    var contentElement = document.createElement("div");
     contentElement.innerHTML = options.template;
 
     $compile(contentElement)(scope);
@@ -53,15 +53,15 @@ function popoverSrv($compile, $rootScope, $timeout) {
         target: options.element,
         content: contentElement,
         position: options.position,
-        classes: options.classNames || 'drop-popover',
+        classes: options.classNames || "drop-popover",
         openOn: options.openOn,
         hoverCloseDelay: 200,
         tetherOptions: {
-          constraints: [{to: 'scrollParent', attachment: 'together'}]
+          constraints: [{ to: "scrollParent", attachment: "together" }]
         }
       });
 
-      drop.on('close', () => {
+      drop.on("close", () => {
         cleanUp();
       });
 
@@ -78,5 +78,4 @@ function popoverSrv($compile, $rootScope, $timeout) {
   };
 }
 
-coreModule.service('popoverSrv', popoverSrv);
-
+coreModule.service("popoverSrv", popoverSrv);

+ 47 - 41
public/app/core/services/search_srv.ts

@@ -1,8 +1,8 @@
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
-import impressionSrv from 'app/core/services/impression_srv';
-import store from 'app/core/store';
-import { contextSrv } from 'app/core/services/context_srv';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
+import impressionSrv from "app/core/services/impression_srv";
+import store from "app/core/store";
+import { contextSrv } from "app/core/services/context_srv";
 
 export class SearchSrv {
   recentIsOpen: boolean;
@@ -10,21 +10,21 @@ export class SearchSrv {
 
   /** @ngInject */
   constructor(private backendSrv, private $q) {
-    this.recentIsOpen = store.getBool('search.sections.recent', true);
-    this.starredIsOpen = store.getBool('search.sections.starred', true);
+    this.recentIsOpen = store.getBool("search.sections.recent", true);
+    this.starredIsOpen = store.getBool("search.sections.starred", true);
   }
 
   private getRecentDashboards(sections) {
     return this.queryForRecentDashboards().then(result => {
       if (result.length > 0) {
-        sections['recent'] = {
-          title: 'Recent Boards',
-          icon: 'fa fa-clock-o',
+        sections["recent"] = {
+          title: "Recent Boards",
+          icon: "fa fa-clock-o",
           score: -1,
           removable: true,
           expanded: this.recentIsOpen,
           toggle: this.toggleRecent.bind(this),
-          items: result,
+          items: result
         };
       }
     });
@@ -37,9 +37,11 @@ export class SearchSrv {
     }
 
     return this.backendSrv.search({ dashboardIds: dashIds }).then(result => {
-      return dashIds.map(orderId => {
-        return _.find(result, { id: orderId });
-      }).filter(hit => hit && !hit.isStarred)
+      return dashIds
+        .map(orderId => {
+          return _.find(result, { id: orderId });
+        })
+        .filter(hit => hit && !hit.isStarred)
         .map(hit => {
           return this.transformToViewModel(hit);
         });
@@ -48,7 +50,7 @@ export class SearchSrv {
 
   private toggleRecent(section) {
     this.recentIsOpen = section.expanded = !section.expanded;
-    store.set('search.sections.recent', this.recentIsOpen);
+    store.set("search.sections.recent", this.recentIsOpen);
 
     if (!section.expanded || section.items.length) {
       return Promise.resolve(section);
@@ -62,7 +64,7 @@ export class SearchSrv {
 
   private toggleStarred(section) {
     this.starredIsOpen = section.expanded = !section.expanded;
-    store.set('search.sections.starred', this.starredIsOpen);
+    store.set("search.sections.starred", this.starredIsOpen);
     return Promise.resolve(section);
   }
 
@@ -71,22 +73,22 @@ export class SearchSrv {
       return Promise.resolve();
     }
 
-    return this.backendSrv.search({starred: true, limit: 5}).then(result => {
+    return this.backendSrv.search({ starred: true, limit: 5 }).then(result => {
       if (result.length > 0) {
-        sections['starred'] = {
-          title: 'Starred Boards',
-          icon: 'fa fa-star-o',
+        sections["starred"] = {
+          title: "Starred Boards",
+          icon: "fa fa-star-o",
           score: -2,
           expanded: this.starredIsOpen,
           toggle: this.toggleStarred.bind(this),
-          items: result.map(this.transformToViewModel),
+          items: result.map(this.transformToViewModel)
         };
       }
     });
   }
 
   private transformToViewModel(hit) {
-    hit.url = 'dashboard/db/' + hit.slug;
+    hit.url = "dashboard/db/" + hit.slug;
     return hit;
   }
 
@@ -94,8 +96,10 @@ export class SearchSrv {
     let sections: any = {};
     let promises = [];
     let query = _.clone(options);
-    let hasFilters = options.query ||
-      (options.tag && options.tag.length > 0) || options.starred ||
+    let hasFilters =
+      options.query ||
+      (options.tag && options.tag.length > 0) ||
+      options.starred ||
       (options.folderIds && options.folderIds.length > 0);
 
     if (!options.skipRecent && !hasFilters) {
@@ -111,12 +115,14 @@ export class SearchSrv {
       query.folderIds = [0];
     }
 
-    promises.push(this.backendSrv.search(query).then(results => {
-      return this.handleSearchResult(sections, results);
-    }));
+    promises.push(
+      this.backendSrv.search(query).then(results => {
+        return this.handleSearchResult(sections, results);
+      })
+    );
 
     return this.$q.all(promises).then(() => {
-      return _.sortBy(_.values(sections), 'score');
+      return _.sortBy(_.values(sections), "score");
     });
   }
 
@@ -127,7 +133,7 @@ export class SearchSrv {
 
     // create folder index
     for (let hit of results) {
-      if (hit.type === 'dash-folder') {
+      if (hit.type === "dash-folder") {
         sections[hit.id] = {
           id: hit.id,
           title: hit.title,
@@ -136,14 +142,14 @@ export class SearchSrv {
           toggle: this.toggleFolder.bind(this),
           url: `dashboards/folder/${hit.id}/${hit.slug}`,
           slug: hit.slug,
-          icon: 'fa fa-folder',
-          score: _.keys(sections).length,
+          icon: "fa fa-folder",
+          score: _.keys(sections).length
         };
       }
     }
 
     for (let hit of results) {
-      if (hit.type === 'dash-folder') {
+      if (hit.type === "dash-folder") {
         continue;
       }
 
@@ -156,18 +162,18 @@ export class SearchSrv {
             url: `dashboards/folder/${hit.folderId}/${hit.folderSlug}`,
             slug: hit.slug,
             items: [],
-            icon: 'fa fa-folder-open',
+            icon: "fa fa-folder-open",
             toggle: this.toggleFolder.bind(this),
-            score: _.keys(sections).length,
+            score: _.keys(sections).length
           };
         } else {
           section = {
             id: 0,
-            title: 'Root',
+            title: "Root",
             items: [],
-            icon: 'fa fa-folder-open',
+            icon: "fa fa-folder-open",
             toggle: this.toggleFolder.bind(this),
-            score: _.keys(sections).length,
+            score: _.keys(sections).length
           };
         }
         // add section
@@ -181,14 +187,14 @@ export class SearchSrv {
 
   private toggleFolder(section) {
     section.expanded = !section.expanded;
-    section.icon = section.expanded ? 'fa fa-folder-open' : 'fa fa-folder';
+    section.icon = section.expanded ? "fa fa-folder-open" : "fa fa-folder";
 
     if (section.items.length) {
       return Promise.resolve(section);
     }
 
     let query = {
-      folderIds: [section.id],
+      folderIds: [section.id]
     };
 
     return this.backendSrv.search(query).then(results => {
@@ -198,8 +204,8 @@ export class SearchSrv {
   }
 
   getDashboardTags() {
-    return this.backendSrv.get('/api/dashboards/tags');
+    return this.backendSrv.get("/api/dashboards/tags");
   }
 }
 
-coreModule.service('searchSrv', SearchSrv);
+coreModule.service("searchSrv", SearchSrv);

+ 4 - 5
public/app/core/services/timer.ts

@@ -1,5 +1,5 @@
-import _ from 'lodash';
-import coreModule from 'app/core/core_module';
+import _ from "lodash";
+import coreModule from "app/core/core_module";
 
 // This service really just tracks a list of $timeout promises to give us a
 // method for cancelling them all when we need to
@@ -7,8 +7,7 @@ export class Timer {
   timers = [];
 
   /** @ngInject */
-  constructor(private $timeout) {
-  }
+  constructor(private $timeout) {}
 
   register(promise) {
     this.timers.push(promise);
@@ -28,4 +27,4 @@ export class Timer {
   }
 }
 
-coreModule.service('timer', Timer);
+coreModule.service("timer", Timer);

+ 7 - 8
public/app/core/services/util_srv.ts

@@ -1,18 +1,17 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
+import coreModule from "app/core/core_module";
+import appEvents from "app/core/app_events";
 
 export class UtilSrv {
   modalScope: any;
 
   /** @ngInject */
-  constructor(private $rootScope, private $modal) {
-  }
+  constructor(private $rootScope, private $modal) {}
 
   init() {
-    appEvents.on('show-modal', this.showModal.bind(this), this.$rootScope);
-    appEvents.on('hide-modal', this.hideModal.bind(this), this.$rootScope);
+    appEvents.on("show-modal", this.showModal.bind(this), this.$rootScope);
+    appEvents.on("hide-modal", this.hideModal.bind(this), this.$rootScope);
   }
 
   hideModal() {
@@ -47,9 +46,9 @@ export class UtilSrv {
     });
 
     Promise.resolve(modal).then(function(modalEl) {
-      modalEl.modal('show');
+      modalEl.modal("show");
     });
   }
 }
 
-coreModule.service('utilSrv', UtilSrv);
+coreModule.service("utilSrv", UtilSrv);

+ 28 - 18
public/app/core/specs/backend_srv_specs.ts

@@ -1,26 +1,36 @@
-import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
-import 'app/core/services/backend_srv';
+import {
+  describe,
+  beforeEach,
+  it,
+  expect,
+  angularMocks
+} from "test/lib/common";
+import "app/core/services/backend_srv";
 
-describe('backend_srv', function() {
+describe("backend_srv", function() {
   var _backendSrv;
   var _httpBackend;
 
-  beforeEach(angularMocks.module('grafana.core'));
-  beforeEach(angularMocks.module('grafana.services'));
-  beforeEach(angularMocks.inject(function ($httpBackend, $http, backendSrv) {
-    _httpBackend = $httpBackend;
-    _backendSrv = backendSrv;
-  }));
+  beforeEach(angularMocks.module("grafana.core"));
+  beforeEach(angularMocks.module("grafana.services"));
+  beforeEach(
+    angularMocks.inject(function($httpBackend, $http, backendSrv) {
+      _httpBackend = $httpBackend;
+      _backendSrv = backendSrv;
+    })
+  );
 
-  describe('when handling errors', function() {
-    it('should return the http status code', function(done) {
-      _httpBackend.whenGET('gateway-error').respond(502);
-      _backendSrv.datasourceRequest({
-        url: 'gateway-error'
-      }).catch(function(err) {
-        expect(err.status).to.be(502);
-        done();
-      });
+  describe("when handling errors", function() {
+    it("should return the http status code", function(done) {
+      _httpBackend.whenGET("gateway-error").respond(502);
+      _backendSrv
+        .datasourceRequest({
+          url: "gateway-error"
+        })
+        .catch(function(err) {
+          expect(err.status).to.be(502);
+          done();
+        });
       _httpBackend.flush();
     });
   });

+ 71 - 53
public/app/core/specs/datemath.jest.ts

@@ -1,37 +1,40 @@
-import sinon from 'sinon';
+import sinon from "sinon";
 
-import * as dateMath from 'app/core/utils/datemath';
-import moment from 'moment';
-import _ from 'lodash';
+import * as dateMath from "app/core/utils/datemath";
+import moment from "moment";
+import _ from "lodash";
 
 describe("DateMath", () => {
-  var spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
-  var anchor =  '2014-01-01T06:06:06.666Z';
+  var spans = ["s", "m", "h", "d", "w", "M", "y"];
+  var anchor = "2014-01-01T06:06:06.666Z";
   var unix = moment(anchor).valueOf();
-  var format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
+  var format = "YYYY-MM-DDTHH:mm:ss.SSSZ";
   var clock;
 
-  describe('errors', () => {
-    it('should return undefined if passed something falsy', () => {
+  describe("errors", () => {
+    it("should return undefined if passed something falsy", () => {
       expect(dateMath.parse(false)).toBe(undefined);
     });
 
-    it('should return undefined if I pass an operator besides [+-/]', () => {
-      expect(dateMath.parse('now&1d')).toBe(undefined);
+    it("should return undefined if I pass an operator besides [+-/]", () => {
+      expect(dateMath.parse("now&1d")).toBe(undefined);
     });
 
-    it('should return undefined if I pass a unit besides' + spans.toString(), () => {
-      expect(dateMath.parse('now+5f')).toBe(undefined);
-    });
+    it(
+      "should return undefined if I pass a unit besides" + spans.toString(),
+      () => {
+        expect(dateMath.parse("now+5f")).toBe(undefined);
+      }
+    );
 
-    it('should return undefined if rounding unit is not 1', () => {
-      expect(dateMath.parse('now/2y')).toBe(undefined);
-      expect(dateMath.parse('now/0.5y')).toBe(undefined);
+    it("should return undefined if rounding unit is not 1", () => {
+      expect(dateMath.parse("now/2y")).toBe(undefined);
+      expect(dateMath.parse("now/0.5y")).toBe(undefined);
     });
 
-    it('should not go into an infinite loop when missing a unit', () => {
-      expect(dateMath.parse('now-0')).toBe(undefined);
-      expect(dateMath.parse('now-00')).toBe(undefined);
+    it("should not go into an infinite loop when missing a unit", () => {
+      expect(dateMath.parse("now-0")).toBe(undefined);
+      expect(dateMath.parse("now-00")).toBe(undefined);
     });
   });
 
@@ -42,19 +45,29 @@ describe("DateMath", () => {
     expected.setSeconds(0);
     expected.setMilliseconds(0);
 
-    var startOfDay = dateMath.parse('now/d', false).valueOf();
+    var startOfDay = dateMath.parse("now/d", false).valueOf();
     expect(startOfDay).toBe(expected.getTime());
   });
 
   it("now/d on a utc dashboard should be start of the current day in UTC time", () => {
     var today = new Date();
-    var expected = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), 0, 0, 0, 0));
-
-    var startOfDay = dateMath.parse('now/d', false, 'utc').valueOf();
+    var expected = new Date(
+      Date.UTC(
+        today.getUTCFullYear(),
+        today.getUTCMonth(),
+        today.getUTCDate(),
+        0,
+        0,
+        0,
+        0
+      )
+    );
+
+    var startOfDay = dateMath.parse("now/d", false, "utc").valueOf();
     expect(startOfDay).toBe(expected.getTime());
   });
 
-  describe('subtraction', () => {
+  describe("subtraction", () => {
     var now;
     var anchored;
 
@@ -64,16 +77,20 @@ describe("DateMath", () => {
       anchored = moment(anchor);
     });
 
-    _.each(spans, (span) => {
-      var nowEx = 'now-5' + span;
-      var thenEx =  anchor + '||-5' + span;
+    _.each(spans, span => {
+      var nowEx = "now-5" + span;
+      var thenEx = anchor + "||-5" + span;
 
-      it('should return 5' + span + ' ago', () => {
-        expect(dateMath.parse(nowEx).format(format)).toEqual(now.subtract(5, span).format(format));
+      it("should return 5" + span + " ago", () => {
+        expect(dateMath.parse(nowEx).format(format)).toEqual(
+          now.subtract(5, span).format(format)
+        );
       });
 
-      it('should return 5' + span + ' before ' + anchor, () => {
-        expect(dateMath.parse(thenEx).format(format)).toEqual(anchored.subtract(5, span).format(format));
+      it("should return 5" + span + " before " + anchor, () => {
+        expect(dateMath.parse(thenEx).format(format)).toEqual(
+          anchored.subtract(5, span).format(format)
+        );
       });
     });
 
@@ -82,7 +99,7 @@ describe("DateMath", () => {
     });
   });
 
-  describe('rounding', () => {
+  describe("rounding", () => {
     var now;
 
     beforeEach(() => {
@@ -90,13 +107,17 @@ describe("DateMath", () => {
       now = moment();
     });
 
-    _.each(spans, (span) => {
-      it('should round now to the beginning of the ' + span, function () {
-        expect(dateMath.parse('now/' + span).format(format)).toEqual(now.startOf(span).format(format));
+    _.each(spans, span => {
+      it("should round now to the beginning of the " + span, function() {
+        expect(dateMath.parse("now/" + span).format(format)).toEqual(
+          now.startOf(span).format(format)
+        );
       });
 
-      it('should round now to the end of the ' + span, function () {
-        expect(dateMath.parse('now/' + span, true).format(format)).toEqual(now.endOf(span).format(format));
+      it("should round now to the end of the " + span, function() {
+        expect(dateMath.parse("now/" + span, true).format(format)).toEqual(
+          now.endOf(span).format(format)
+        );
       });
     });
 
@@ -105,32 +126,29 @@ describe("DateMath", () => {
     });
   });
 
-  describe('isValid', () => {
-    it('should return false when invalid date text', () => {
-      expect(dateMath.isValid('asd')).toBe(false);
+  describe("isValid", () => {
+    it("should return false when invalid date text", () => {
+      expect(dateMath.isValid("asd")).toBe(false);
     });
-    it('should return true when valid date text', () => {
-      expect(dateMath.isValid('now-1h')).toBe(true);
+    it("should return true when valid date text", () => {
+      expect(dateMath.isValid("now-1h")).toBe(true);
     });
   });
 
-  describe('relative time to date parsing', function() {
-    it('should handle negative time', function() {
-      var date = dateMath.parseDateMath('-2d', moment([2014, 1, 5]));
+  describe("relative time to date parsing", function() {
+    it("should handle negative time", function() {
+      var date = dateMath.parseDateMath("-2d", moment([2014, 1, 5]));
       expect(date.valueOf()).toEqual(moment([2014, 1, 3]).valueOf());
     });
 
-    it('should handle multiple math expressions', function() {
-      var date = dateMath.parseDateMath('-2d-6h', moment([2014, 1, 5]));
+    it("should handle multiple math expressions", function() {
+      var date = dateMath.parseDateMath("-2d-6h", moment([2014, 1, 5]));
       expect(date.valueOf()).toEqual(moment([2014, 1, 2, 18]).valueOf());
     });
 
-    it('should return false when invalid expression', function() {
-      var date = dateMath.parseDateMath('2', moment([2014, 1, 5]));
+    it("should return false when invalid expression", function() {
+      var date = dateMath.parseDateMath("2", moment([2014, 1, 5]));
       expect(date).toEqual(undefined);
     });
   });
-
 });
-
-

+ 20 - 20
public/app/core/specs/emitter.jest.ts

@@ -1,28 +1,26 @@
-import {Emitter} from '../utils/emitter';
+import { Emitter } from "../utils/emitter";
 
 describe("Emitter", () => {
-
-  describe('given 2 subscribers', () => {
-
-    it('should notfiy subscribers', () => {
+  describe("given 2 subscribers", () => {
+    it("should notfiy subscribers", () => {
       var events = new Emitter();
       var sub1Called = false;
       var sub2Called = false;
 
-      events.on('test', () => {
+      events.on("test", () => {
         sub1Called = true;
       });
-      events.on('test', () => {
+      events.on("test", () => {
         sub2Called = true;
       });
 
-      events.emit('test', null);
+      events.emit("test", null);
 
       expect(sub1Called).toBe(true);
       expect(sub2Called).toBe(true);
     });
 
-    it('when subscribing twice', () => {
+    it("when subscribing twice", () => {
       var events = new Emitter();
       var sub1Called = 0;
 
@@ -30,35 +28,37 @@ describe("Emitter", () => {
         sub1Called += 1;
       }
 
-      events.on('test', handler);
-      events.on('test', handler);
+      events.on("test", handler);
+      events.on("test", handler);
 
-      events.emit('test', null);
+      events.emit("test", null);
 
       expect(sub1Called).toBe(2);
     });
 
-    it('should handle errors', () => {
+    it("should handle errors", () => {
       var events = new Emitter();
       var sub1Called = 0;
       var sub2Called = 0;
 
-      events.on('test', () => {
+      events.on("test", () => {
         sub1Called++;
-        throw {message: "hello"};
+        throw { message: "hello" };
       });
 
-      events.on('test', () => {
+      events.on("test", () => {
         sub2Called++;
       });
 
-      try { events.emit('test', null); } catch (_) { }
-      try { events.emit('test', null); } catch (_) {}
+      try {
+        events.emit("test", null);
+      } catch (_) {}
+      try {
+        events.emit("test", null);
+      } catch (_) {}
 
       expect(sub1Called).toBe(2);
       expect(sub2Called).toBe(0);
     });
   });
 });
-
-

+ 15 - 15
public/app/core/specs/flatten.jest.ts

@@ -1,22 +1,22 @@
-import flatten from 'app/core/utils/flatten';
+import flatten from "app/core/utils/flatten";
 
 describe("flatten", () => {
-
-  it('should return flatten object', () => {
-    var flattened = flatten({
-      level1: 'level1-value',
-      deeper: {
-        level2: 'level2-value',
+  it("should return flatten object", () => {
+    var flattened = flatten(
+      {
+        level1: "level1-value",
         deeper: {
-          level3: 'level3-value'
+          level2: "level2-value",
+          deeper: {
+            level3: "level3-value"
+          }
         }
-      }
-    }, null);
+      },
+      null
+    );
 
-    expect(flattened['level1']).toBe('level1-value');
-    expect(flattened['deeper.level2']).toBe('level2-value');
-    expect(flattened['deeper.deeper.level3']).toBe('level3-value');
+    expect(flattened["level1"]).toBe("level1-value");
+    expect(flattened["deeper.level2"]).toBe("level2-value");
+    expect(flattened["deeper.deeper.level3"]).toBe("level3-value");
   });
-
 });
-

+ 9 - 9
public/app/core/specs/global_event_srv.jest.ts

@@ -1,23 +1,23 @@
-import { GlobalEventSrv } from 'app/core/services/global_event_srv';
-import { beforeEach } from 'test/lib/common';
+import { GlobalEventSrv } from "app/core/services/global_event_srv";
+import { beforeEach } from "test/lib/common";
 
-jest.mock('app/core/config', () => {
+jest.mock("app/core/config", () => {
   return {
-    appSubUrl: '/subUrl'
+    appSubUrl: "/subUrl"
   };
 });
 
-describe('GlobalEventSrv', () => {
+describe("GlobalEventSrv", () => {
   let searchSrv;
 
   beforeEach(() => {
     searchSrv = new GlobalEventSrv(null, null);
   });
 
-  describe('With /subUrl as appSubUrl', () => {
-    it('/subUrl should be stripped', () => {
-        const urlWithoutMaster = searchSrv.stripBaseFromUrl('/subUrl/grafana/');
-        expect(urlWithoutMaster).toBe('/grafana/');
+  describe("With /subUrl as appSubUrl", () => {
+    it("/subUrl should be stripped", () => {
+      const urlWithoutMaster = searchSrv.stripBaseFromUrl("/subUrl/grafana/");
+      expect(urlWithoutMaster).toBe("/grafana/");
     });
   });
 });

+ 206 - 198
public/app/core/specs/kbn.jest.ts

@@ -1,27 +1,29 @@
-import kbn from '../utils/kbn';
-import * as dateMath from '../utils/datemath';
-import moment from 'moment';
+import kbn from "../utils/kbn";
+import * as dateMath from "../utils/datemath";
+import moment from "moment";
 
-describe('unit format menu', function() {
+describe("unit format menu", function() {
   var menu = kbn.getUnitFormats();
   menu.map(function(submenu) {
-
-    describe('submenu ' + submenu.text, function() {
-
-      it('should have a title', function() {
-        expect(typeof submenu.text).toBe('string');
+    describe("submenu " + submenu.text, function() {
+      it("should have a title", function() {
+        expect(typeof submenu.text).toBe("string");
       });
 
-      it('should have a submenu', function() {
+      it("should have a submenu", function() {
         expect(Array.isArray(submenu.submenu)).toBe(true);
       });
 
       submenu.submenu.map(function(entry) {
-        describe('entry ' + entry.text, function() {
-          it('should have a title', function() { expect(typeof entry.text).toBe('string'); });
-          it('should have a format', function() { expect(typeof entry.value).toBe('string'); });
-          it('should have a valid format', function() {
-            expect(typeof kbn.valueFormats[entry.value]).toBe('function');
+        describe("entry " + entry.text, function() {
+          it("should have a title", function() {
+            expect(typeof entry.text).toBe("string");
+          });
+          it("should have a format", function() {
+            expect(typeof entry.value).toBe("string");
+          });
+          it("should have a valid format", function() {
+            expect(typeof kbn.valueFormats[entry.value]).toBe("function");
           });
         });
       });
@@ -30,318 +32,324 @@ describe('unit format menu', function() {
 });
 
 function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
-
-  describe('value format: ' + desc, function() {
-    it('should translate ' + value + ' as ' + result, function() {
-      var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
+  describe("value format: " + desc, function() {
+    it("should translate " + value + " as " + result, function() {
+      var scaledDecimals =
+        tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
       var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals);
       expect(str).toBe(result);
     });
   });
-
 }
 
-describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms');
-describeValueFormat('ms', 100, 1, 0, '100 ms');
-describeValueFormat('ms', 1250, 10, 0, '1.25 s');
-describeValueFormat('ms', 1250, 300, 0, '1.3 s');
-describeValueFormat('ms', 65150, 10000, 0, '1.1 min');
-describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour');
-describeValueFormat('ms', 651500000, 150000000, 0, '8 day');
-
-describeValueFormat('none', 2.75e-10, 0, 10, '3e-10');
-describeValueFormat('none', 0, 0, 2, '0');
-describeValueFormat('dB', 10, 1000, 2, '10.00 dB');
-
-describeValueFormat('percent',  0, 0, 0, '0%');
-describeValueFormat('percent', 53, 0, 1, '53.0%');
-describeValueFormat('percentunit', 0.0, 0, 0, '0%');
-describeValueFormat('percentunit', 0.278, 0, 1, '27.8%');
-describeValueFormat('percentunit', 1.0, 0, 0, '100%');
-
-describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42');
-describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K');
-describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M');
-
-describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA');
-
-describeValueFormat('ns', 25, 1, 0, '25 ns');
-describeValueFormat('ns', 2558, 50, 0, '2.56 µs');
-
-describeValueFormat('ops', 123, 1, 0, '123 ops');
-describeValueFormat('rps', 456000, 1000, -1, '456K rps');
-describeValueFormat('rps', 123456789, 1000000, 2, '123.457M rps');
-describeValueFormat('wps', 789000000, 1000000, -1, '789M wps');
-describeValueFormat('iops', 11000000000, 1000000000, -1, '11B iops');
-
-describeValueFormat('s', 1.23456789e-7, 1e-10, 8, '123.5 ns');
-describeValueFormat('s', 1.23456789e-4, 1e-7, 5, '123.5 µs');
-describeValueFormat('s', 1.23456789e-3, 1e-6, 4, '1.235 ms');
-describeValueFormat('s', 1.23456789e-2, 1e-5, 3, '12.35 ms');
-describeValueFormat('s', 1.23456789e-1, 1e-4, 2, '123.5 ms');
-describeValueFormat('s', 24, 1, 0, '24 s');
-describeValueFormat('s', 246, 1, 0, '4.1 min');
-describeValueFormat('s', 24567, 100, 0, '6.82 hour');
-describeValueFormat('s', 24567890, 10000, 0, '40.62 week');
-describeValueFormat('s', 24567890000, 1000000, 0, '778.53 year');
-
-describeValueFormat('m', 24, 1, 0, '24 min');
-describeValueFormat('m', 246, 10, 0, '4.1 hour');
-describeValueFormat('m', 6545, 10, 0, '4.55 day');
-describeValueFormat('m', 24567, 100, 0, '2.44 week');
-describeValueFormat('m', 24567892, 10000, 0, '46.7 year');
-
-describeValueFormat('h', 21, 1, 0, '21 hour');
-describeValueFormat('h', 145, 1, 0, '6.04 day');
-describeValueFormat('h', 1234, 100, 0, '7.3 week');
-describeValueFormat('h', 9458, 1000, 0, '1.08 year');
-
-describeValueFormat('d', 3, 1, 0, '3 day');
-describeValueFormat('d', 245, 100, 0, '35 week');
-describeValueFormat('d', 2456, 10, 0, '6.73 year');
-
-describe('date time formats', function() {
-  it('should format as iso date', function() {
+describeValueFormat("ms", 0.0024, 0.0005, 4, "0.0024 ms");
+describeValueFormat("ms", 100, 1, 0, "100 ms");
+describeValueFormat("ms", 1250, 10, 0, "1.25 s");
+describeValueFormat("ms", 1250, 300, 0, "1.3 s");
+describeValueFormat("ms", 65150, 10000, 0, "1.1 min");
+describeValueFormat("ms", 6515000, 1500000, 0, "1.8 hour");
+describeValueFormat("ms", 651500000, 150000000, 0, "8 day");
+
+describeValueFormat("none", 2.75e-10, 0, 10, "3e-10");
+describeValueFormat("none", 0, 0, 2, "0");
+describeValueFormat("dB", 10, 1000, 2, "10.00 dB");
+
+describeValueFormat("percent", 0, 0, 0, "0%");
+describeValueFormat("percent", 53, 0, 1, "53.0%");
+describeValueFormat("percentunit", 0.0, 0, 0, "0%");
+describeValueFormat("percentunit", 0.278, 0, 1, "27.8%");
+describeValueFormat("percentunit", 1.0, 0, 0, "100%");
+
+describeValueFormat("currencyUSD", 7.42, 10000, 2, "$7.42");
+describeValueFormat("currencyUSD", 1532.82, 1000, 1, "$1.53K");
+describeValueFormat("currencyUSD", 18520408.7, 10000000, 0, "$19M");
+
+describeValueFormat("bytes", -1.57e308, -1.57e308, 2, "NA");
+
+describeValueFormat("ns", 25, 1, 0, "25 ns");
+describeValueFormat("ns", 2558, 50, 0, "2.56 µs");
+
+describeValueFormat("ops", 123, 1, 0, "123 ops");
+describeValueFormat("rps", 456000, 1000, -1, "456K rps");
+describeValueFormat("rps", 123456789, 1000000, 2, "123.457M rps");
+describeValueFormat("wps", 789000000, 1000000, -1, "789M wps");
+describeValueFormat("iops", 11000000000, 1000000000, -1, "11B iops");
+
+describeValueFormat("s", 1.23456789e-7, 1e-10, 8, "123.5 ns");
+describeValueFormat("s", 1.23456789e-4, 1e-7, 5, "123.5 µs");
+describeValueFormat("s", 1.23456789e-3, 1e-6, 4, "1.235 ms");
+describeValueFormat("s", 1.23456789e-2, 1e-5, 3, "12.35 ms");
+describeValueFormat("s", 1.23456789e-1, 1e-4, 2, "123.5 ms");
+describeValueFormat("s", 24, 1, 0, "24 s");
+describeValueFormat("s", 246, 1, 0, "4.1 min");
+describeValueFormat("s", 24567, 100, 0, "6.82 hour");
+describeValueFormat("s", 24567890, 10000, 0, "40.62 week");
+describeValueFormat("s", 24567890000, 1000000, 0, "778.53 year");
+
+describeValueFormat("m", 24, 1, 0, "24 min");
+describeValueFormat("m", 246, 10, 0, "4.1 hour");
+describeValueFormat("m", 6545, 10, 0, "4.55 day");
+describeValueFormat("m", 24567, 100, 0, "2.44 week");
+describeValueFormat("m", 24567892, 10000, 0, "46.7 year");
+
+describeValueFormat("h", 21, 1, 0, "21 hour");
+describeValueFormat("h", 145, 1, 0, "6.04 day");
+describeValueFormat("h", 1234, 100, 0, "7.3 week");
+describeValueFormat("h", 9458, 1000, 0, "1.08 year");
+
+describeValueFormat("d", 3, 1, 0, "3 day");
+describeValueFormat("d", 245, 100, 0, "35 week");
+describeValueFormat("d", 2456, 10, 0, "6.73 year");
+
+describe("date time formats", function() {
+  it("should format as iso date", function() {
     var str = kbn.valueFormats.dateTimeAsIso(1505634997920, 1);
-    expect(str).toBe(moment(1505634997920).format('YYYY-MM-DD HH:mm:ss'));
+    expect(str).toBe(moment(1505634997920).format("YYYY-MM-DD HH:mm:ss"));
   });
 
-  it('should format as iso date and skip date when today', function() {
+  it("should format as iso date and skip date when today", function() {
     var now = moment();
     var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1);
     expect(str).toBe(now.format("HH:mm:ss"));
   });
 
-  it('should format as US date', function() {
+  it("should format as US date", function() {
     var str = kbn.valueFormats.dateTimeAsUS(1505634997920, 1);
-    expect(str).toBe(moment(1505634997920).format('MM/DD/YYYY h:mm:ss a'));
+    expect(str).toBe(moment(1505634997920).format("MM/DD/YYYY h:mm:ss a"));
   });
 
-  it('should format as US date and skip date when today', function() {
+  it("should format as US date and skip date when today", function() {
     var now = moment();
     var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1);
     expect(str).toBe(now.format("h:mm:ss a"));
   });
 
-  it('should format as from now with days', function() {
-    var daysAgo = moment().add(-7, 'd');
+  it("should format as from now with days", function() {
+    var daysAgo = moment().add(-7, "d");
     var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1);
-    expect(str).toBe('7 days ago');
+    expect(str).toBe("7 days ago");
   });
 
-  it('should format as from now with minutes', function() {
-    var daysAgo = moment().add(-2, 'm');
+  it("should format as from now with minutes", function() {
+    var daysAgo = moment().add(-2, "m");
     var str = kbn.valueFormats.dateTimeFromNow(daysAgo.valueOf(), 1);
-    expect(str).toBe('2 minutes ago');
+    expect(str).toBe("2 minutes ago");
   });
 });
 
-describe('kbn.toFixed and negative decimals', function() {
-  it('should treat as zero decimals', function() {
+describe("kbn.toFixed and negative decimals", function() {
+  it("should treat as zero decimals", function() {
     var str = kbn.toFixed(186.123, -2);
-    expect(str).toBe('186');
+    expect(str).toBe("186");
   });
 });
 
-describe('kbn ms format when scaled decimals is null do not use it', function() {
-  it('should use specified decimals', function() {
-    var str = kbn.valueFormats['ms'](10000086.123, 1, null);
-    expect(str).toBe('2.8 hour');
+describe("kbn ms format when scaled decimals is null do not use it", function() {
+  it("should use specified decimals", function() {
+    var str = kbn.valueFormats["ms"](10000086.123, 1, null);
+    expect(str).toBe("2.8 hour");
   });
 });
 
-describe('kbn kbytes format when scaled decimals is null do not use it', function() {
-  it('should use specified decimals', function() {
-    var str = kbn.valueFormats['kbytes'](10000000, 3, null);
-    expect(str).toBe('9.537 GiB');
+describe("kbn kbytes format when scaled decimals is null do not use it", function() {
+  it("should use specified decimals", function() {
+    var str = kbn.valueFormats["kbytes"](10000000, 3, null);
+    expect(str).toBe("9.537 GiB");
   });
 });
 
-describe('kbn deckbytes format when scaled decimals is null do not use it', function() {
-  it('should use specified decimals', function() {
-    var str = kbn.valueFormats['deckbytes'](10000000, 3, null);
-    expect(str).toBe('10.000 GB');
+describe("kbn deckbytes format when scaled decimals is null do not use it", function() {
+  it("should use specified decimals", function() {
+    var str = kbn.valueFormats["deckbytes"](10000000, 3, null);
+    expect(str).toBe("10.000 GB");
   });
 });
 
-describe('kbn roundValue', function() {
-  it('should should handle null value', function() {
+describe("kbn roundValue", function() {
+  it("should should handle null value", function() {
     var str = kbn.roundValue(null, 2);
     expect(str).toBe(null);
   });
-  it('should round value', function() {
+  it("should round value", function() {
     var str = kbn.roundValue(200.877, 2);
     expect(str).toBe(200.88);
   });
 });
 
-describe('calculateInterval', function() {
-  it('1h 100 resultion', function() {
-    var range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') };
+describe("calculateInterval", function() {
+  it("1h 100 resultion", function() {
+    var range = { from: dateMath.parse("now-1h"), to: dateMath.parse("now") };
     var res = kbn.calculateInterval(range, 100, null);
-    expect(res.interval).toBe('30s');
+    expect(res.interval).toBe("30s");
   });
 
-  it('10m 1600 resolution', function() {
-    var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
+  it("10m 1600 resolution", function() {
+    var range = { from: dateMath.parse("now-10m"), to: dateMath.parse("now") };
     var res = kbn.calculateInterval(range, 1600, null);
-    expect(res.interval).toBe('500ms');
+    expect(res.interval).toBe("500ms");
     expect(res.intervalMs).toBe(500);
   });
 
-  it('fixed user min interval', function() {
-    var range = {from: dateMath.parse('now-10m'), to: dateMath.parse('now')};
-    var res = kbn.calculateInterval(range, 1600, '10s');
-    expect(res.interval).toBe('10s');
+  it("fixed user min interval", function() {
+    var range = { from: dateMath.parse("now-10m"), to: dateMath.parse("now") };
+    var res = kbn.calculateInterval(range, 1600, "10s");
+    expect(res.interval).toBe("10s");
     expect(res.intervalMs).toBe(10000);
   });
 
-  it('short time range and user low limit', function() {
-    var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
-    var res = kbn.calculateInterval(range, 1600, '>10s');
-    expect(res.interval).toBe('10s');
+  it("short time range and user low limit", function() {
+    var range = { from: dateMath.parse("now-10m"), to: dateMath.parse("now") };
+    var res = kbn.calculateInterval(range, 1600, ">10s");
+    expect(res.interval).toBe("10s");
   });
 
-  it('large time range and user low limit', function() {
-    var range = {from: dateMath.parse('now-14d'), to: dateMath.parse('now')};
-    var res = kbn.calculateInterval(range, 1000, '>10s');
-    expect(res.interval).toBe('20m');
+  it("large time range and user low limit", function() {
+    var range = { from: dateMath.parse("now-14d"), to: dateMath.parse("now") };
+    var res = kbn.calculateInterval(range, 1000, ">10s");
+    expect(res.interval).toBe("20m");
   });
 
-  it('10s 900 resolution and user low limit in ms', function() {
-    var range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') };
-    var res = kbn.calculateInterval(range, 900, '>15ms');
-    expect(res.interval).toBe('15ms');
+  it("10s 900 resolution and user low limit in ms", function() {
+    var range = { from: dateMath.parse("now-10s"), to: dateMath.parse("now") };
+    var res = kbn.calculateInterval(range, 900, ">15ms");
+    expect(res.interval).toBe("15ms");
   });
 
-  it('1d 1 resolution', function() {
-    var range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') };
+  it("1d 1 resolution", function() {
+    var range = { from: dateMath.parse("now-1d"), to: dateMath.parse("now") };
     var res = kbn.calculateInterval(range, 1, null);
-    expect(res.interval).toBe('1d');
+    expect(res.interval).toBe("1d");
     expect(res.intervalMs).toBe(86400000);
   });
 
-  it('86399s 1 resolution', function() {
-    var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
+  it("86399s 1 resolution", function() {
+    var range = {
+      from: dateMath.parse("now-86390s"),
+      to: dateMath.parse("now")
+    };
     var res = kbn.calculateInterval(range, 1, null);
-    expect(res.interval).toBe('12h');
+    expect(res.interval).toBe("12h");
     expect(res.intervalMs).toBe(43200000);
   });
 });
 
-describe('hex', function() {
-  it('positive integer', function() {
+describe("hex", function() {
+  it("positive integer", function() {
     var str = kbn.valueFormats.hex(100, 0);
-    expect(str).toBe('64');
+    expect(str).toBe("64");
   });
-  it('negative integer', function() {
+  it("negative integer", function() {
     var str = kbn.valueFormats.hex(-100, 0);
-    expect(str).toBe('-64');
+    expect(str).toBe("-64");
   });
-  it('null', function() {
+  it("null", function() {
     var str = kbn.valueFormats.hex(null, 0);
-    expect(str).toBe('');
+    expect(str).toBe("");
   });
-  it('positive float', function() {
+  it("positive float", function() {
     var str = kbn.valueFormats.hex(50.52, 1);
-    expect(str).toBe('32.8');
+    expect(str).toBe("32.8");
   });
-  it('negative float', function() {
+  it("negative float", function() {
     var str = kbn.valueFormats.hex(-50.333, 2);
-    expect(str).toBe('-32.547AE147AE14');
+    expect(str).toBe("-32.547AE147AE14");
   });
 });
 
-describe('hex 0x', function() {
-  it('positive integeter', function() {
-    var str = kbn.valueFormats.hex0x(7999,0);
-    expect(str).toBe('0x1F3F');
+describe("hex 0x", function() {
+  it("positive integeter", function() {
+    var str = kbn.valueFormats.hex0x(7999, 0);
+    expect(str).toBe("0x1F3F");
   });
-  it('negative integer', function() {
-    var str = kbn.valueFormats.hex0x(-584,0);
-    expect(str).toBe('-0x248');
+  it("negative integer", function() {
+    var str = kbn.valueFormats.hex0x(-584, 0);
+    expect(str).toBe("-0x248");
   });
-  it('null', function() {
+  it("null", function() {
     var str = kbn.valueFormats.hex0x(null, 0);
-    expect(str).toBe('');
+    expect(str).toBe("");
   });
-  it('positive float', function() {
+  it("positive float", function() {
     var str = kbn.valueFormats.hex0x(74.443, 3);
-    expect(str).toBe('0x4A.716872B020C4');
+    expect(str).toBe("0x4A.716872B020C4");
   });
-  it('negative float', function() {
+  it("negative float", function() {
     var str = kbn.valueFormats.hex0x(-65.458, 1);
-    expect(str).toBe('-0x41.8');
+    expect(str).toBe("-0x41.8");
   });
 });
 
-describe('duration', function() {
-  it('null', function() {
+describe("duration", function() {
+  it("null", function() {
     var str = kbn.toDuration(null, 0, "millisecond");
-    expect(str).toBe('');
+    expect(str).toBe("");
   });
-  it('0 milliseconds', function() {
+  it("0 milliseconds", function() {
     var str = kbn.toDuration(0, 0, "millisecond");
-    expect(str).toBe('0 milliseconds');
+    expect(str).toBe("0 milliseconds");
   });
-  it('1 millisecond', function() {
+  it("1 millisecond", function() {
     var str = kbn.toDuration(1, 0, "millisecond");
-    expect(str).toBe('1 millisecond');
+    expect(str).toBe("1 millisecond");
   });
-  it('-1 millisecond', function() {
+  it("-1 millisecond", function() {
     var str = kbn.toDuration(-1, 0, "millisecond");
-    expect(str).toBe('1 millisecond ago');
+    expect(str).toBe("1 millisecond ago");
   });
-  it('seconds', function() {
+  it("seconds", function() {
     var str = kbn.toDuration(1, 0, "second");
-    expect(str).toBe('1 second');
+    expect(str).toBe("1 second");
   });
-  it('minutes', function() {
+  it("minutes", function() {
     var str = kbn.toDuration(1, 0, "minute");
-    expect(str).toBe('1 minute');
+    expect(str).toBe("1 minute");
   });
-  it('hours', function() {
+  it("hours", function() {
     var str = kbn.toDuration(1, 0, "hour");
-    expect(str).toBe('1 hour');
+    expect(str).toBe("1 hour");
   });
-  it('days', function() {
+  it("days", function() {
     var str = kbn.toDuration(1, 0, "day");
-    expect(str).toBe('1 day');
+    expect(str).toBe("1 day");
   });
-  it('weeks', function() {
+  it("weeks", function() {
     var str = kbn.toDuration(1, 0, "week");
-    expect(str).toBe('1 week');
+    expect(str).toBe("1 week");
   });
-  it('months', function() {
+  it("months", function() {
     var str = kbn.toDuration(1, 0, "month");
-    expect(str).toBe('1 month');
+    expect(str).toBe("1 month");
   });
-  it('years', function() {
+  it("years", function() {
     var str = kbn.toDuration(1, 0, "year");
-    expect(str).toBe('1 year');
+    expect(str).toBe("1 year");
   });
-  it('decimal days', function() {
+  it("decimal days", function() {
     var str = kbn.toDuration(1.5, 2, "day");
-    expect(str).toBe('1 day, 12 hours, 0 minutes');
+    expect(str).toBe("1 day, 12 hours, 0 minutes");
   });
-  it('decimal months', function() {
+  it("decimal months", function() {
     var str = kbn.toDuration(1.5, 3, "month");
-    expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours');
+    expect(str).toBe("1 month, 2 weeks, 1 day, 0 hours");
   });
-  it('no decimals', function() {
+  it("no decimals", function() {
     var str = kbn.toDuration(38898367008, 0, "millisecond");
-    expect(str).toBe('1 year');
+    expect(str).toBe("1 year");
   });
-  it('1 decimal', function() {
+  it("1 decimal", function() {
     var str = kbn.toDuration(38898367008, 1, "millisecond");
-    expect(str).toBe('1 year, 2 months');
+    expect(str).toBe("1 year, 2 months");
   });
-  it('too many decimals', function() {
+  it("too many decimals", function() {
     var str = kbn.toDuration(38898367008, 20, "millisecond");
-    expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds');
+    expect(str).toBe(
+      "1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds"
+    );
   });
-  it('floating point error', function() {
+  it("floating point error", function() {
     var str = kbn.toDuration(36993906007, 8, "millisecond");
-    expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds');
+    expect(str).toBe(
+      "1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds"
+    );
   });
 });

+ 122 - 154
public/app/core/specs/manage_dashboards.jest.ts

@@ -1,11 +1,11 @@
-import { ManageDashboardsCtrl } from 'app/core/components/manage_dashboards/manage_dashboards';
-import { SearchSrv } from 'app/core/services/search_srv';
-import q from 'q';
+import { ManageDashboardsCtrl } from "app/core/components/manage_dashboards/manage_dashboards";
+import { SearchSrv } from "app/core/services/search_srv";
+import q from "q";
 
-describe('ManageDashboards', () => {
+describe("ManageDashboards", () => {
   let ctrl;
 
-  describe('when browsing dashboards', () => {
+  describe("when browsing dashboards", () => {
     beforeEach(() => {
       const response = [
         {
@@ -17,7 +17,7 @@ describe('ManageDashboards', () => {
               id: 399,
               title: "Dashboard Test",
               url: "dashboard/db/dashboard-test",
-              icon: 'fa fa-folder',
+              icon: "fa fa-folder",
               tags: [],
               isStarred: false,
               folderId: 410,
@@ -31,7 +31,7 @@ describe('ManageDashboards', () => {
         {
           id: 0,
           title: "Root",
-          icon: 'fa fa-folder-open',
+          icon: "fa fa-folder-open",
           uri: "db/something-else",
           type: "dash-db",
           items: [
@@ -39,20 +39,20 @@ describe('ManageDashboards', () => {
               id: 500,
               title: "Dashboard Test",
               url: "dashboard/db/dashboard-test",
-              icon: 'fa fa-folder',
+              icon: "fa fa-folder",
               tags: [],
               isStarred: false
             }
           ],
           tags: [],
-          isStarred: false,
+          isStarred: false
         }
       ];
       ctrl = createCtrlWithStubs(response);
       return ctrl.getDashboards();
     });
 
-    it('should set checked to false on all sections and children', () => {
+    it("should set checked to false on all sections and children", () => {
       expect(ctrl.sections.length).toEqual(2);
       expect(ctrl.sections[0].checked).toEqual(false);
       expect(ctrl.sections[0].items[0].checked).toEqual(false);
@@ -62,7 +62,7 @@ describe('ManageDashboards', () => {
     });
   });
 
-  describe('when browsing dashboards for a folder', () => {
+  describe("when browsing dashboards for a folder", () => {
     beforeEach(() => {
       const response = [
         {
@@ -74,7 +74,7 @@ describe('ManageDashboards', () => {
               id: 399,
               title: "Dashboard Test",
               url: "dashboard/db/dashboard-test",
-              icon: 'fa fa-folder',
+              icon: "fa fa-folder",
               tags: [],
               isStarred: false,
               folderId: 410,
@@ -91,12 +91,12 @@ describe('ManageDashboards', () => {
       return ctrl.getDashboards();
     });
 
-    it('should set hide header to true on section', () => {
+    it("should set hide header to true on section", () => {
       expect(ctrl.sections[0].hideHeader).toBeTruthy();
     });
   });
 
-  describe('when searching dashboards', () => {
+  describe("when searching dashboards", () => {
     beforeEach(() => {
       const response = [
         {
@@ -108,7 +108,7 @@ describe('ManageDashboards', () => {
               id: 399,
               title: "Dashboard Test",
               url: "dashboard/db/dashboard-test",
-              icon: 'fa fa-folder',
+              icon: "fa fa-folder",
               tags: [],
               isStarred: false,
               folderId: 410,
@@ -119,7 +119,7 @@ describe('ManageDashboards', () => {
               id: 500,
               title: "Dashboard Test",
               url: "dashboard/db/dashboard-test",
-              icon: 'fa fa-folder',
+              icon: "fa fa-folder",
               tags: [],
               folderId: 499,
               isStarred: false
@@ -131,96 +131,96 @@ describe('ManageDashboards', () => {
       ctrl = createCtrlWithStubs(response);
     });
 
-    describe('with query filter', () => {
+    describe("with query filter", () => {
       beforeEach(() => {
-        ctrl.query.query = 'd';
+        ctrl.query.query = "d";
         ctrl.canMove = true;
         ctrl.canDelete = true;
         ctrl.selectAllChecked = true;
         return ctrl.getDashboards();
       });
 
-      it('should set checked to false on all sections and children', () => {
+      it("should set checked to false on all sections and children", () => {
         expect(ctrl.sections.length).toEqual(1);
         expect(ctrl.sections[0].checked).toEqual(false);
         expect(ctrl.sections[0].items[0].checked).toEqual(false);
         expect(ctrl.sections[0].items[1].checked).toEqual(false);
       });
 
-      it('should uncheck select all', () => {
+      it("should uncheck select all", () => {
         expect(ctrl.selectAllChecked).toBeFalsy();
       });
 
-      it('should disable Move To button', () => {
+      it("should disable Move To button", () => {
         expect(ctrl.canMove).toBeFalsy();
       });
 
-      it('should disable delete button', () => {
+      it("should disable delete button", () => {
         expect(ctrl.canDelete).toBeFalsy();
       });
 
-      it('should have active filters', () => {
+      it("should have active filters", () => {
         expect(ctrl.hasFilters).toBeTruthy();
       });
 
-      describe('when select all is checked', () => {
+      describe("when select all is checked", () => {
         beforeEach(() => {
           ctrl.selectAllChecked = true;
           ctrl.onSelectAllChanged();
         });
 
-        it('should select all dashboards', () => {
+        it("should select all dashboards", () => {
           expect(ctrl.sections[0].checked).toBeFalsy();
           expect(ctrl.sections[0].items[0].checked).toBeTruthy();
           expect(ctrl.sections[0].items[1].checked).toBeTruthy();
         });
 
-        it('should enable Move To button', () => {
+        it("should enable Move To button", () => {
           expect(ctrl.canMove).toBeTruthy();
         });
 
-        it('should enable delete button', () => {
+        it("should enable delete button", () => {
           expect(ctrl.canDelete).toBeTruthy();
         });
 
-        describe('when clearing filters', () => {
+        describe("when clearing filters", () => {
           beforeEach(() => {
             return ctrl.clearFilters();
           });
 
-          it('should reset query filter', () => {
-            expect(ctrl.query.query).toEqual('');
+          it("should reset query filter", () => {
+            expect(ctrl.query.query).toEqual("");
           });
         });
       });
     });
 
-    describe('with tag filter', () => {
+    describe("with tag filter", () => {
       beforeEach(() => {
-        return ctrl.filterByTag('test');
+        return ctrl.filterByTag("test");
       });
 
-      it('should set tag filter', () => {
+      it("should set tag filter", () => {
         expect(ctrl.sections.length).toEqual(1);
-        expect(ctrl.query.tag[0]).toEqual('test');
+        expect(ctrl.query.tag[0]).toEqual("test");
       });
 
-      it('should have active filters', () => {
+      it("should have active filters", () => {
         expect(ctrl.hasFilters).toBeTruthy();
       });
 
-      describe('when clearing filters', () => {
+      describe("when clearing filters", () => {
         beforeEach(() => {
           return ctrl.clearFilters();
         });
 
-        it('should reset tag filter', () => {
+        it("should reset tag filter", () => {
           expect(ctrl.query.tag.length).toEqual(0);
         });
       });
     });
 
-    describe('with starred filter', () => {
+    describe("with starred filter", () => {
       beforeEach(() => {
         const yesOption: any = ctrl.starredFilterOptions[1];
 
@@ -228,187 +228,171 @@ describe('ManageDashboards', () => {
         return ctrl.onStarredFilterChange();
       });
 
-      it('should set starred filter', () => {
+      it("should set starred filter", () => {
         expect(ctrl.sections.length).toEqual(1);
         expect(ctrl.query.starred).toEqual(true);
       });
 
-      it('should have active filters', () => {
+      it("should have active filters", () => {
         expect(ctrl.hasFilters).toBeTruthy();
       });
 
-      describe('when clearing filters', () => {
+      describe("when clearing filters", () => {
         beforeEach(() => {
           return ctrl.clearFilters();
         });
 
-        it('should reset starred filter', () => {
+        it("should reset starred filter", () => {
           expect(ctrl.query.starred).toEqual(false);
         });
       });
     });
   });
 
-  describe('when selecting dashboards', () => {
+  describe("when selecting dashboards", () => {
     let ctrl;
 
     beforeEach(() => {
       ctrl = createCtrlWithStubs([]);
     });
 
-    describe('and no dashboards are selected', () => {
+    describe("and no dashboards are selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            items: [
-              { id: 2, checked: false }
-            ],
+            items: [{ id: 2, checked: false }],
             checked: false
           },
           {
             id: 0,
-            items: [
-              { id: 3, checked: false }
-            ],
+            items: [{ id: 3, checked: false }],
             checked: false
           }
         ];
         ctrl.selectionChanged();
       });
 
-      it('should disable Move To button', () => {
+      it("should disable Move To button", () => {
         expect(ctrl.canMove).toBeFalsy();
       });
 
-      it('should disable delete button', () => {
+      it("should disable delete button", () => {
         expect(ctrl.canDelete).toBeFalsy();
       });
 
-      describe('when select all is checked', () => {
+      describe("when select all is checked", () => {
         beforeEach(() => {
           ctrl.selectAllChecked = true;
           ctrl.onSelectAllChanged();
         });
 
-        it('should select all folders and dashboards', () => {
+        it("should select all folders and dashboards", () => {
           expect(ctrl.sections[0].checked).toBeTruthy();
           expect(ctrl.sections[0].items[0].checked).toBeTruthy();
           expect(ctrl.sections[1].checked).toBeTruthy();
           expect(ctrl.sections[1].items[0].checked).toBeTruthy();
         });
 
-        it('should enable Move To button', () => {
+        it("should enable Move To button", () => {
           expect(ctrl.canMove).toBeTruthy();
         });
 
-        it('should enable delete button', () => {
+        it("should enable delete button", () => {
           expect(ctrl.canDelete).toBeTruthy();
         });
       });
     });
 
-    describe('and all folders and dashboards are selected', () => {
+    describe("and all folders and dashboards are selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            items: [
-              { id: 2, checked: true }
-            ],
+            items: [{ id: 2, checked: true }],
             checked: true
           },
           {
             id: 0,
-            items: [
-              { id: 3, checked: true }
-            ],
+            items: [{ id: 3, checked: true }],
             checked: true
           }
         ];
         ctrl.selectionChanged();
       });
 
-      it('should enable Move To button', () => {
+      it("should enable Move To button", () => {
         expect(ctrl.canMove).toBeTruthy();
       });
 
-      it('should enable delete button', () => {
+      it("should enable delete button", () => {
         expect(ctrl.canDelete).toBeTruthy();
       });
 
-      describe('when select all is unchecked', () => {
+      describe("when select all is unchecked", () => {
         beforeEach(() => {
           ctrl.selectAllChecked = false;
           ctrl.onSelectAllChanged();
         });
 
-        it('should uncheck all checked folders and dashboards', () => {
+        it("should uncheck all checked folders and dashboards", () => {
           expect(ctrl.sections[0].checked).toBeFalsy();
           expect(ctrl.sections[0].items[0].checked).toBeFalsy();
           expect(ctrl.sections[1].checked).toBeFalsy();
           expect(ctrl.sections[1].items[0].checked).toBeFalsy();
         });
 
-        it('should disable Move To button', () => {
+        it("should disable Move To button", () => {
           expect(ctrl.canMove).toBeFalsy();
         });
 
-        it('should disable delete button', () => {
+        it("should disable delete button", () => {
           expect(ctrl.canDelete).toBeFalsy();
         });
       });
     });
 
-    describe('and one dashboard in root is selected', () => {
+    describe("and one dashboard in root is selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            title: 'folder',
-            items: [
-              { id: 2, checked: false }
-            ],
+            title: "folder",
+            items: [{ id: 2, checked: false }],
             checked: false
           },
           {
             id: 0,
-            title: 'Root',
-            items: [
-              { id: 3, checked: true }
-            ],
+            title: "Root",
+            items: [{ id: 3, checked: true }],
             checked: false
           }
         ];
         ctrl.selectionChanged();
       });
 
-      it('should enable Move To button', () => {
+      it("should enable Move To button", () => {
         expect(ctrl.canMove).toBeTruthy();
       });
 
-      it('should enable delete button', () => {
+      it("should enable delete button", () => {
         expect(ctrl.canDelete).toBeTruthy();
       });
     });
 
-    describe('and one child dashboard is selected', () => {
+    describe("and one child dashboard is selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            title: 'folder',
-            items: [
-              { id: 2, checked: true }
-            ],
+            title: "folder",
+            items: [{ id: 2, checked: true }],
             checked: false
           },
           {
             id: 0,
-            title: 'Root',
-            items: [
-              { id: 3, checked: false }
-            ],
+            title: "Root",
+            items: [{ id: 3, checked: false }],
             checked: false
           }
         ];
@@ -416,32 +400,28 @@ describe('ManageDashboards', () => {
         ctrl.selectionChanged();
       });
 
-      it('should enable Move To button', () => {
+      it("should enable Move To button", () => {
         expect(ctrl.canMove).toBeTruthy();
       });
 
-      it('should enable delete button', () => {
+      it("should enable delete button", () => {
         expect(ctrl.canDelete).toBeTruthy();
       });
     });
 
-    describe('and one child dashboard and one dashboard is selected', () => {
+    describe("and one child dashboard and one dashboard is selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            title: 'folder',
-            items: [
-              { id: 2, checked: true }
-            ],
+            title: "folder",
+            items: [{ id: 2, checked: true }],
             checked: false
           },
           {
             id: 0,
-            title: 'Root',
-            items: [
-              { id: 3, checked: true }
-            ],
+            title: "Root",
+            items: [{ id: 3, checked: true }],
             checked: false
           }
         ];
@@ -449,40 +429,34 @@ describe('ManageDashboards', () => {
         ctrl.selectionChanged();
       });
 
-      it('should enable Move To button', () => {
+      it("should enable Move To button", () => {
         expect(ctrl.canMove).toBeTruthy();
       });
 
-      it('should enable delete button', () => {
+      it("should enable delete button", () => {
         expect(ctrl.canDelete).toBeTruthy();
       });
     });
 
-    describe('and one child dashboard and one folder is selected', () => {
+    describe("and one child dashboard and one folder is selected", () => {
       beforeEach(() => {
         ctrl.sections = [
           {
             id: 1,
-            title: 'folder',
-            items: [
-              { id: 2, checked: false }
-            ],
+            title: "folder",
+            items: [{ id: 2, checked: false }],
             checked: true
           },
           {
             id: 3,
-            title: 'folder',
-            items: [
-              { id: 4, checked: true }
-            ],
+            title: "folder",
+            items: [{ id: 4, checked: true }],
             checked: false
           },
           {
             id: 0,
-            title: 'Root',
-            items: [
-              { id: 3, checked: false }
-            ],
+            title: "Root",
+            items: [{ id: 3, checked: false }],
             checked: false
           }
         ];
@@ -490,17 +464,17 @@ describe('ManageDashboards', () => {
         ctrl.selectionChanged();
       });
 
-      it('should enable Move To button', () => {
+      it("should enable Move To button", () => {
         expect(ctrl.canMove).toBeTruthy();
       });
 
-      it('should enable delete button', () => {
+      it("should enable delete button", () => {
         expect(ctrl.canDelete).toBeTruthy();
       });
     });
   });
 
-  describe('when deleting dashboards', () => {
+  describe("when deleting dashboards", () => {
     let toBeDeleted: any;
 
     beforeEach(() => {
@@ -509,28 +483,22 @@ describe('ManageDashboards', () => {
       ctrl.sections = [
         {
           id: 1,
-          title: 'folder',
-          items: [
-            { id: 2, checked: true, slug: 'folder-dash' }
-          ],
+          title: "folder",
+          items: [{ id: 2, checked: true, slug: "folder-dash" }],
           checked: true,
-          slug: 'folder'
+          slug: "folder"
         },
         {
           id: 3,
-          title: 'folder-2',
-          items: [
-            { id: 3, checked: true, slug: 'folder-2-dash' }
-          ],
+          title: "folder-2",
+          items: [{ id: 3, checked: true, slug: "folder-2-dash" }],
           checked: false,
-          slug: 'folder-2'
+          slug: "folder-2"
         },
         {
           id: 0,
-          title: 'Root',
-          items: [
-            { id: 3, checked: true, slug: 'root-dash' }
-          ],
+          title: "Root",
+          items: [{ id: 3, checked: true, slug: "root-dash" }],
           checked: true
         }
       ];
@@ -538,57 +506,53 @@ describe('ManageDashboards', () => {
       toBeDeleted = ctrl.getFoldersAndDashboardsToDelete();
     });
 
-    it('should return 1 folder', () => {
+    it("should return 1 folder", () => {
       expect(toBeDeleted.folders.length).toEqual(1);
     });
 
-    it('should return 2 dashboards', () => {
+    it("should return 2 dashboards", () => {
       expect(toBeDeleted.dashboards.length).toEqual(2);
     });
 
-    it('should filter out children if parent is checked', () => {
-      expect(toBeDeleted.folders[0]).toEqual('folder');
+    it("should filter out children if parent is checked", () => {
+      expect(toBeDeleted.folders[0]).toEqual("folder");
     });
 
-    it('should not filter out children if parent not is checked', () => {
-      expect(toBeDeleted.dashboards[0]).toEqual('folder-2-dash');
+    it("should not filter out children if parent not is checked", () => {
+      expect(toBeDeleted.dashboards[0]).toEqual("folder-2-dash");
     });
 
-    it('should not filter out children if parent is checked and root', () => {
-      expect(toBeDeleted.dashboards[1]).toEqual('root-dash');
+    it("should not filter out children if parent is checked and root", () => {
+      expect(toBeDeleted.dashboards[1]).toEqual("root-dash");
     });
   });
 
-  describe('when moving dashboards', () => {
+  describe("when moving dashboards", () => {
     beforeEach(() => {
       ctrl = createCtrlWithStubs([]);
 
       ctrl.sections = [
         {
           id: 1,
-          title: 'folder',
-          items: [
-            { id: 2, checked: true, slug: 'dash' }
-          ],
+          title: "folder",
+          items: [{ id: 2, checked: true, slug: "dash" }],
           checked: false,
-          slug: 'folder'
+          slug: "folder"
         },
         {
           id: 0,
-          title: 'Root',
-          items: [
-            { id: 3, checked: true, slug: 'dash-2' }
-          ],
+          title: "Root",
+          items: [{ id: 3, checked: true, slug: "dash-2" }],
           checked: false
         }
       ];
     });
 
-    it('should get selected dashboards', () => {
+    it("should get selected dashboards", () => {
       const toBeMove = ctrl.getDashboardsToMove();
       expect(toBeMove.length).toEqual(2);
-      expect(toBeMove[0]).toEqual('dash');
-      expect(toBeMove[1]).toEqual('dash-2');
+      expect(toBeMove[0]).toEqual("dash");
+      expect(toBeMove[1]).toEqual("dash-2");
     });
   });
 });
@@ -603,5 +567,9 @@ function createCtrlWithStubs(searchResponse: any, tags?: any) {
     }
   };
 
-  return new ManageDashboardsCtrl({}, { getNav: () => { } }, <SearchSrv>searchSrvStub);
+  return new ManageDashboardsCtrl(
+    {},
+    { getNav: () => {} },
+    <SearchSrv>searchSrvStub
+  );
 }

+ 23 - 16
public/app/core/specs/org_switcher.jest.ts

@@ -1,38 +1,45 @@
-import {OrgSwitchCtrl} from '../components/org_switcher';
-import q from 'q';
+import { OrgSwitchCtrl } from "../components/org_switcher";
+import q from "q";
 
-jest.mock('app/core/services/context_srv', () => ({
+jest.mock("app/core/services/context_srv", () => ({
   contextSrv: {
-    user: {orgId: 1}
+    user: { orgId: 1 }
   }
 }));
 
-describe('OrgSwitcher', () => {
-  describe('when switching org', () => {
+describe("OrgSwitcher", () => {
+  describe("when switching org", () => {
     let expectedHref;
     let expectedUsingUrl;
 
     beforeEach(() => {
-
       const backendSrvStub: any = {
-        get: (url) => { return q.resolve([]); },
-        post: (url) => { expectedUsingUrl = url; return q.resolve({}); }
+        get: url => {
+          return q.resolve([]);
+        },
+        post: url => {
+          expectedUsingUrl = url;
+          return q.resolve({});
+        }
       };
 
       const orgSwitcherCtrl = new OrgSwitchCtrl(backendSrvStub);
 
-      orgSwitcherCtrl.getWindowLocationHref = () => 'http://localhost:3000?orgId=1&from=now-3h&to=now';
-      orgSwitcherCtrl.setWindowLocationHref = (href) => expectedHref = href;
+      orgSwitcherCtrl.getWindowLocationHref = () =>
+        "http://localhost:3000?orgId=1&from=now-3h&to=now";
+      orgSwitcherCtrl.setWindowLocationHref = href => (expectedHref = href);
 
-      return orgSwitcherCtrl.setUsingOrg({orgId: 2});
+      return orgSwitcherCtrl.setUsingOrg({ orgId: 2 });
     });
 
-    it('should switch orgId in call to backend', () => {
-      expect(expectedUsingUrl).toBe('/api/user/using/2');
+    it("should switch orgId in call to backend", () => {
+      expect(expectedUsingUrl).toBe("/api/user/using/2");
     });
 
-    it('should switch orgId in url', () => {
-      expect(expectedHref).toBe('http://localhost:3000?orgId=2&from=now-3h&to=now');
+    it("should switch orgId in url", () => {
+      expect(expectedHref).toBe(
+        "http://localhost:3000?orgId=2&from=now-3h&to=now"
+      );
     });
   });
 });

+ 74 - 62
public/app/core/specs/rangeutil.jest.ts

@@ -1,108 +1,120 @@
-import * as rangeUtil from 'app/core/utils/rangeutil';
-import _ from 'lodash';
-import moment from 'moment';
+import * as rangeUtil from "app/core/utils/rangeutil";
+import _ from "lodash";
+import moment from "moment";
 
 describe("rangeUtil", () => {
-
   describe("Can get range grouped list of ranges", () => {
-    it('when custom settings should return default range list', () => {
-      var groups = rangeUtil.getRelativeTimesList({time_options: []}, 'Last 5 minutes');
+    it("when custom settings should return default range list", () => {
+      var groups = rangeUtil.getRelativeTimesList(
+        { time_options: [] },
+        "Last 5 minutes"
+      );
       expect(_.keys(groups).length).toBe(4);
       expect(groups[3][0].active).toBe(true);
     });
   });
 
   describe("Can get range text described", () => {
-    it('should handle simple old expression with only amount and unit', () => {
-      var info = rangeUtil.describeTextRange('5m');
-      expect(info.display).toBe('Last 5 minutes');
+    it("should handle simple old expression with only amount and unit", () => {
+      var info = rangeUtil.describeTextRange("5m");
+      expect(info.display).toBe("Last 5 minutes");
     });
 
-    it('should have singular when amount is 1', () => {
-      var info = rangeUtil.describeTextRange('1h');
-      expect(info.display).toBe('Last 1 hour');
+    it("should have singular when amount is 1", () => {
+      var info = rangeUtil.describeTextRange("1h");
+      expect(info.display).toBe("Last 1 hour");
     });
 
-    it('should handle non default amount', () => {
-      var info = rangeUtil.describeTextRange('13h');
-      expect(info.display).toBe('Last 13 hours');
-      expect(info.from).toBe('now-13h');
+    it("should handle non default amount", () => {
+      var info = rangeUtil.describeTextRange("13h");
+      expect(info.display).toBe("Last 13 hours");
+      expect(info.from).toBe("now-13h");
     });
 
-    it('should handle non default future amount', () => {
-      var info = rangeUtil.describeTextRange('+3h');
-      expect(info.display).toBe('Next 3 hours');
-      expect(info.from).toBe('now');
-      expect(info.to).toBe('now+3h');
+    it("should handle non default future amount", () => {
+      var info = rangeUtil.describeTextRange("+3h");
+      expect(info.display).toBe("Next 3 hours");
+      expect(info.from).toBe("now");
+      expect(info.to).toBe("now+3h");
     });
 
-    it('should handle now/d', () => {
-      var info = rangeUtil.describeTextRange('now/d');
-      expect(info.display).toBe('Today so far');
+    it("should handle now/d", () => {
+      var info = rangeUtil.describeTextRange("now/d");
+      expect(info.display).toBe("Today so far");
     });
 
-    it('should handle now/w', () => {
-      var info = rangeUtil.describeTextRange('now/w');
-      expect(info.display).toBe('This week so far');
+    it("should handle now/w", () => {
+      var info = rangeUtil.describeTextRange("now/w");
+      expect(info.display).toBe("This week so far");
     });
 
-    it('should handle now/M', () => {
-      var info = rangeUtil.describeTextRange('now/M');
-      expect(info.display).toBe('This month so far');
+    it("should handle now/M", () => {
+      var info = rangeUtil.describeTextRange("now/M");
+      expect(info.display).toBe("This month so far");
     });
 
-    it('should handle now/y', () => {
-      var info = rangeUtil.describeTextRange('now/y');
-      expect(info.display).toBe('This year so far');
+    it("should handle now/y", () => {
+      var info = rangeUtil.describeTextRange("now/y");
+      expect(info.display).toBe("This year so far");
     });
   });
 
   describe("Can get date range described", () => {
-    it('Date range with simple ranges', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now-1h', to: 'now'});
-      expect(text).toBe('Last 1 hour');
+    it("Date range with simple ranges", () => {
+      var text = rangeUtil.describeTimeRange({ from: "now-1h", to: "now" });
+      expect(text).toBe("Last 1 hour");
     });
 
-    it('Date range with rounding ranges', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now'});
-      expect(text).toBe('now/d+6h to now');
+    it("Date range with rounding ranges", () => {
+      var text = rangeUtil.describeTimeRange({ from: "now/d+6h", to: "now" });
+      expect(text).toBe("now/d+6h to now");
     });
 
-    it('Date range with absolute to now', () => {
-      var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now'});
-      expect(text).toBe('Nov 10, 2014 02:03:04 to a few seconds ago');
+    it("Date range with absolute to now", () => {
+      var text = rangeUtil.describeTimeRange({
+        from: moment([2014, 10, 10, 2, 3, 4]),
+        to: "now"
+      });
+      expect(text).toBe("Nov 10, 2014 02:03:04 to a few seconds ago");
     });
 
-    it('Date range with absolute to relative', () => {
-      var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now-1d'});
-      expect(text).toBe('Nov 10, 2014 02:03:04 to a day ago');
+    it("Date range with absolute to relative", () => {
+      var text = rangeUtil.describeTimeRange({
+        from: moment([2014, 10, 10, 2, 3, 4]),
+        to: "now-1d"
+      });
+      expect(text).toBe("Nov 10, 2014 02:03:04 to a day ago");
     });
 
-    it('Date range with relative to absolute', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now-7d', to: moment([2014,10,10,2,3,4])});
-      expect(text).toBe('7 days ago to Nov 10, 2014 02:03:04');
+    it("Date range with relative to absolute", () => {
+      var text = rangeUtil.describeTimeRange({
+        from: "now-7d",
+        to: moment([2014, 10, 10, 2, 3, 4])
+      });
+      expect(text).toBe("7 days ago to Nov 10, 2014 02:03:04");
     });
 
-    it('Date range with non matching default ranges', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now-13h', to: 'now'});
-      expect(text).toBe('Last 13 hours');
+    it("Date range with non matching default ranges", () => {
+      var text = rangeUtil.describeTimeRange({ from: "now-13h", to: "now" });
+      expect(text).toBe("Last 13 hours");
     });
 
-    it('Date range with from and to both are in now-* format', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now-3h'});
-      expect(text).toBe('now-6h to now-3h');
+    it("Date range with from and to both are in now-* format", () => {
+      var text = rangeUtil.describeTimeRange({ from: "now-6h", to: "now-3h" });
+      expect(text).toBe("now-6h to now-3h");
     });
 
-    it('Date range with from and to both are either in now-* or now/* format', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now-3h'});
-      expect(text).toBe('now/d+6h to now-3h');
+    it("Date range with from and to both are either in now-* or now/* format", () => {
+      var text = rangeUtil.describeTimeRange({
+        from: "now/d+6h",
+        to: "now-3h"
+      });
+      expect(text).toBe("now/d+6h to now-3h");
     });
 
-    it('Date range with from and to both are either in now-* or now+* format', () => {
-      var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now+1h'});
-      expect(text).toBe('now-6h to now+1h');
+    it("Date range with from and to both are either in now-* or now+* format", () => {
+      var text = rangeUtil.describeTimeRange({ from: "now-6h", to: "now+1h" });
+      expect(text).toBe("now-6h to now+1h");
     });
-
   });
-
 });

+ 61 - 68
public/app/core/specs/search.jest.ts

@@ -1,73 +1,75 @@
-import { SearchCtrl } from '../components/search/search';
-import { SearchSrv } from '../services/search_srv';
+import { SearchCtrl } from "../components/search/search";
+import { SearchSrv } from "../services/search_srv";
 
-describe('SearchCtrl', () => {
+describe("SearchCtrl", () => {
   const searchSrvStub = {
     search: (options: any) => {},
     getDashboardTags: () => {}
   };
-  let ctrl = new SearchCtrl({$on: () => {}}, {}, {}, <SearchSrv>searchSrvStub);
-
-  describe('Given an empty result', () => {
+  let ctrl = new SearchCtrl(
+    { $on: () => {} },
+    {},
+    {},
+    <SearchSrv>searchSrvStub
+  );
+
+  describe("Given an empty result", () => {
     beforeEach(() => {
       ctrl.results = [];
     });
 
-    describe('When navigating down one step', () => {
+    describe("When navigating down one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
       });
 
-      it('should not navigate', () => {
+      it("should not navigate", () => {
         expect(ctrl.selectedIndex).toBe(0);
       });
     });
 
-    describe('When navigating up one step', () => {
+    describe("When navigating up one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(-1);
       });
 
-      it('should not navigate', () => {
+      it("should not navigate", () => {
         expect(ctrl.selectedIndex).toBe(0);
       });
     });
   });
 
-  describe('Given a result of one selected collapsed folder with no dashboards and a root folder with 2 dashboards', () => {
+  describe("Given a result of one selected collapsed folder with no dashboards and a root folder with 2 dashboards", () => {
     beforeEach(() => {
       ctrl.results = [
         {
           id: 1,
-          title: 'folder',
+          title: "folder",
           items: [],
           selected: true,
           expanded: false,
-          toggle: (i) => i.expanded = !i.expanded
+          toggle: i => (i.expanded = !i.expanded)
         },
         {
           id: 0,
-          title: 'Root',
-          items: [
-            { id: 3, selected: false },
-            { id: 5, selected: false }
-          ],
+          title: "Root",
+          items: [{ id: 3, selected: false }, { id: 5, selected: false }],
           selected: false,
           expanded: true,
-          toggle: (i) => i.expanded = !i.expanded
+          toggle: i => (i.expanded = !i.expanded)
         }
       ];
     });
 
-    describe('When navigating down one step', () => {
+    describe("When navigating down one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
       });
 
-      it('should select first dashboard in root folder', () => {
+      it("should select first dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeTruthy();
@@ -75,14 +77,14 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating down two steps', () => {
+    describe("When navigating down two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
         ctrl.moveSelection(1);
       });
 
-      it('should select last dashboard in root folder', () => {
+      it("should select last dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeFalsy();
@@ -90,7 +92,7 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating down three steps', () => {
+    describe("When navigating down three steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
@@ -98,7 +100,7 @@ describe('SearchCtrl', () => {
         ctrl.moveSelection(1);
       });
 
-      it('should select first folder', () => {
+      it("should select first folder", () => {
         expect(ctrl.results[0].selected).toBeTruthy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeFalsy();
@@ -106,13 +108,13 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating up one step', () => {
+    describe("When navigating up one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(-1);
       });
 
-      it('should select last dashboard in root folder', () => {
+      it("should select last dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeFalsy();
@@ -120,14 +122,14 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating up two steps', () => {
+    describe("When navigating up two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(-1);
         ctrl.moveSelection(-1);
       });
 
-      it('should select first dashboard in root folder', () => {
+      it("should select first dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeTruthy();
@@ -136,41 +138,35 @@ describe('SearchCtrl', () => {
     });
   });
 
-  describe('Given a result of one selected collapsed folder with 2 dashboards and a root folder with 2 dashboards', () => {
+  describe("Given a result of one selected collapsed folder with 2 dashboards and a root folder with 2 dashboards", () => {
     beforeEach(() => {
       ctrl.results = [
         {
           id: 1,
-          title: 'folder',
-          items: [
-            { id: 2, selected: false },
-            { id: 4, selected: false }
-          ],
+          title: "folder",
+          items: [{ id: 2, selected: false }, { id: 4, selected: false }],
           selected: true,
           expanded: false,
-          toggle: (i) => i.expanded = !i.expanded
+          toggle: i => (i.expanded = !i.expanded)
         },
         {
           id: 0,
-          title: 'Root',
-          items: [
-            { id: 3, selected: false },
-            { id: 5, selected: false }
-          ],
+          title: "Root",
+          items: [{ id: 3, selected: false }, { id: 5, selected: false }],
           selected: false,
           expanded: true,
-          toggle: (i) => i.expanded = !i.expanded
+          toggle: i => (i.expanded = !i.expanded)
         }
       ];
     });
 
-    describe('When navigating down one step', () => {
+    describe("When navigating down one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
       });
 
-      it('should select first dashboard in root folder', () => {
+      it("should select first dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
@@ -180,14 +176,14 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating down two steps', () => {
+    describe("When navigating down two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
         ctrl.moveSelection(1);
       });
 
-      it('should select last dashboard in root folder', () => {
+      it("should select last dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
@@ -197,7 +193,7 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating down three steps', () => {
+    describe("When navigating down three steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(1);
@@ -205,7 +201,7 @@ describe('SearchCtrl', () => {
         ctrl.moveSelection(1);
       });
 
-      it('should select first folder', () => {
+      it("should select first folder", () => {
         expect(ctrl.results[0].selected).toBeTruthy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
@@ -215,13 +211,13 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating up one step', () => {
+    describe("When navigating up one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(-1);
       });
 
-      it('should select last dashboard in root folder', () => {
+      it("should select last dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
@@ -231,14 +227,14 @@ describe('SearchCtrl', () => {
       });
     });
 
-    describe('When navigating up two steps', () => {
+    describe("When navigating up two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 0;
         ctrl.moveSelection(-1);
         ctrl.moveSelection(-1);
       });
 
-      it('should select first dashboard in root folder', () => {
+      it("should select first dashboard in root folder", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[1].selected).toBeFalsy();
         expect(ctrl.results[1].items[0].selected).toBeTruthy();
@@ -247,50 +243,47 @@ describe('SearchCtrl', () => {
     });
   });
 
-  describe('Given a result of a search with 2 dashboards where the first is selected', () => {
+  describe("Given a result of a search with 2 dashboards where the first is selected", () => {
     beforeEach(() => {
       ctrl.results = [
         {
           hideHeader: true,
-          items: [
-            { id: 3, selected: true },
-            { id: 5, selected: false }
-          ],
+          items: [{ id: 3, selected: true }, { id: 5, selected: false }],
           selected: false,
           expanded: true,
-          toggle: (i) => i.expanded = !i.expanded
+          toggle: i => (i.expanded = !i.expanded)
         }
       ];
     });
 
-    describe('When navigating down one step', () => {
+    describe("When navigating down one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 1;
         ctrl.moveSelection(1);
       });
 
-      it('should select last dashboard', () => {
+      it("should select last dashboard", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[1].selected).toBeTruthy();
       });
     });
 
-    describe('When navigating down two steps', () => {
+    describe("When navigating down two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 1;
         ctrl.moveSelection(1);
         ctrl.moveSelection(1);
       });
 
-      it('should select first dashboard', () => {
+      it("should select first dashboard", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeTruthy();
         expect(ctrl.results[0].items[1].selected).toBeFalsy();
       });
     });
 
-    describe('When navigating down three steps', () => {
+    describe("When navigating down three steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 1;
         ctrl.moveSelection(1);
@@ -298,34 +291,34 @@ describe('SearchCtrl', () => {
         ctrl.moveSelection(1);
       });
 
-      it('should select last dashboard', () => {
+      it("should select last dashboard", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[1].selected).toBeTruthy();
       });
     });
 
-    describe('When navigating up one step', () => {
+    describe("When navigating up one step", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 1;
         ctrl.moveSelection(-1);
       });
 
-      it('should select last dashboard', () => {
+      it("should select last dashboard", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[1].selected).toBeTruthy();
       });
     });
 
-    describe('When navigating up two steps', () => {
+    describe("When navigating up two steps", () => {
       beforeEach(() => {
         ctrl.selectedIndex = 1;
         ctrl.moveSelection(-1);
         ctrl.moveSelection(-1);
       });
 
-      it('should select first dashboard', () => {
+      it("should select first dashboard", () => {
         expect(ctrl.results[0].selected).toBeFalsy();
         expect(ctrl.results[0].items[0].selected).toBeTruthy();
         expect(ctrl.results[0].items[1].selected).toBeFalsy();

+ 40 - 36
public/app/core/specs/search_results.jest.ts

@@ -1,74 +1,76 @@
-import { SearchResultsCtrl } from '../components/search/search_results';
-import { beforeEach, afterEach } from 'test/lib/common';
-import appEvents from 'app/core/app_events';
+import { SearchResultsCtrl } from "../components/search/search_results";
+import { beforeEach, afterEach } from "test/lib/common";
+import appEvents from "app/core/app_events";
 
-jest.mock('app/core/app_events', () => {
+jest.mock("app/core/app_events", () => {
   return {
     emit: jest.fn<any>()
   };
 });
 
-describe('SearchResultsCtrl', () => {
+describe("SearchResultsCtrl", () => {
   let ctrl;
 
-  describe('when checking an item that is not checked', () => {
-    let item = {checked: false};
+  describe("when checking an item that is not checked", () => {
+    let item = { checked: false };
     let selectionChanged = false;
 
     beforeEach(() => {
       ctrl = new SearchResultsCtrl({});
-      ctrl.onSelectionChanged = () => selectionChanged = true;
+      ctrl.onSelectionChanged = () => (selectionChanged = true);
       ctrl.toggleSelection(item);
     });
 
-    it('should set checked to true', () => {
+    it("should set checked to true", () => {
       expect(item.checked).toBeTruthy();
     });
 
-    it('should trigger selection changed callback', () => {
+    it("should trigger selection changed callback", () => {
       expect(selectionChanged).toBeTruthy();
     });
   });
 
-  describe('when checking an item that is checked', () => {
-    let item = {checked: true};
+  describe("when checking an item that is checked", () => {
+    let item = { checked: true };
     let selectionChanged = false;
 
     beforeEach(() => {
       ctrl = new SearchResultsCtrl({});
-      ctrl.onSelectionChanged = () => selectionChanged = true;
+      ctrl.onSelectionChanged = () => (selectionChanged = true);
       ctrl.toggleSelection(item);
     });
 
-    it('should set checked to false', () => {
+    it("should set checked to false", () => {
       expect(item.checked).toBeFalsy();
     });
 
-    it('should trigger selection changed callback', () => {
+    it("should trigger selection changed callback", () => {
       expect(selectionChanged).toBeTruthy();
     });
   });
 
-  describe('when selecting a tag', () => {
+  describe("when selecting a tag", () => {
     let selectedTag = null;
 
     beforeEach(() => {
       ctrl = new SearchResultsCtrl({});
-      ctrl.onTagSelected = (tag) => selectedTag = tag;
-      ctrl.selectTag('tag-test');
+      ctrl.onTagSelected = tag => (selectedTag = tag);
+      ctrl.selectTag("tag-test");
     });
 
-    it('should trigger tag selected callback', () => {
-      expect(selectedTag["$tag"]).toBe('tag-test');
+    it("should trigger tag selected callback", () => {
+      expect(selectedTag["$tag"]).toBe("tag-test");
     });
   });
 
-  describe('when toggle a collapsed folder', () => {
+  describe("when toggle a collapsed folder", () => {
     let folderExpanded = false;
 
     beforeEach(() => {
       ctrl = new SearchResultsCtrl({});
-      ctrl.onFolderExpanding = () => { folderExpanded = true; };
+      ctrl.onFolderExpanding = () => {
+        folderExpanded = true;
+      };
 
       let folder = {
         expanded: false,
@@ -78,17 +80,19 @@ describe('SearchResultsCtrl', () => {
       ctrl.toggleFolderExpand(folder);
     });
 
-    it('should trigger folder expanding callback', () => {
+    it("should trigger folder expanding callback", () => {
       expect(folderExpanded).toBeTruthy();
     });
   });
 
-  describe('when toggle an expanded folder', () => {
+  describe("when toggle an expanded folder", () => {
     let folderExpanded = false;
 
     beforeEach(() => {
       ctrl = new SearchResultsCtrl({});
-      ctrl.onFolderExpanding = () => { folderExpanded = true; };
+      ctrl.onFolderExpanding = () => {
+        folderExpanded = true;
+      };
 
       let folder = {
         expanded: true,
@@ -98,37 +102,37 @@ describe('SearchResultsCtrl', () => {
       ctrl.toggleFolderExpand(folder);
     });
 
-    it('should not trigger folder expanding callback', () => {
+    it("should not trigger folder expanding callback", () => {
       expect(folderExpanded).toBeFalsy();
     });
   });
 
-  describe('when clicking on a link in search result', () => {
-    const dashPath = 'dashboard/path';
-    const $location = { path: () =>  dashPath};
+  describe("when clicking on a link in search result", () => {
+    const dashPath = "dashboard/path";
+    const $location = { path: () => dashPath };
     const appEventsMock = appEvents as any;
 
-    describe('with the same url as current path', () => {
+    describe("with the same url as current path", () => {
       beforeEach(() => {
         ctrl = new SearchResultsCtrl($location);
-        const item = { url: dashPath};
+        const item = { url: dashPath };
         ctrl.onItemClick(item);
       });
 
-      it('should close the search', () => {
+      it("should close the search", () => {
         expect(appEventsMock.emit.mock.calls.length).toBe(1);
-        expect(appEventsMock.emit.mock.calls[0][0]).toBe('hide-dash-search');
+        expect(appEventsMock.emit.mock.calls[0][0]).toBe("hide-dash-search");
       });
     });
 
-    describe('with a different url than current path', () => {
+    describe("with a different url than current path", () => {
       beforeEach(() => {
         ctrl = new SearchResultsCtrl($location);
-        const item = { url: 'another/path'};
+        const item = { url: "another/path" };
         ctrl.onItemClick(item);
       });
 
-      it('should do nothing', () => {
+      it("should do nothing", () => {
         expect(appEventsMock.emit.mock.calls.length).toBe(0);
       });
     });

+ 91 - 87
public/app/core/specs/search_srv.jest.ts

@@ -1,23 +1,23 @@
-import { SearchSrv } from 'app/core/services/search_srv';
-import { BackendSrvMock } from 'test/mocks/backend_srv';
-import impressionSrv from 'app/core/services/impression_srv';
-import { contextSrv } from 'app/core/services/context_srv';
-import { beforeEach } from 'test/lib/common';
+import { SearchSrv } from "app/core/services/search_srv";
+import { BackendSrvMock } from "test/mocks/backend_srv";
+import impressionSrv from "app/core/services/impression_srv";
+import { contextSrv } from "app/core/services/context_srv";
+import { beforeEach } from "test/lib/common";
 
-jest.mock('app/core/store', () => {
+jest.mock("app/core/store", () => {
   return {
     getBool: jest.fn(),
-    set: jest.fn(),
+    set: jest.fn()
   };
 });
 
-jest.mock('app/core/services/impression_srv', () => {
+jest.mock("app/core/services/impression_srv", () => {
   return {
-    getDashboardOpened: jest.fn,
+    getDashboardOpened: jest.fn
   };
 });
 
-describe('SearchSrv', () => {
+describe("SearchSrv", () => {
   let searchSrv, backendSrvMock;
 
   beforeEach(() => {
@@ -28,52 +28,57 @@ describe('SearchSrv', () => {
     impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]);
   });
 
-  describe('With recent dashboards', () => {
+  describe("With recent dashboards", () => {
     let results;
 
     beforeEach(() => {
       backendSrvMock.search = jest
         .fn()
         .mockReturnValueOnce(
-          Promise.resolve([{ id: 2, title: 'second but first' }, { id: 1, title: 'first but second' }]),
+          Promise.resolve([
+            { id: 2, title: "second but first" },
+            { id: 1, title: "first but second" }
+          ])
         )
         .mockReturnValue(Promise.resolve([]));
 
       impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1, 2]);
 
-      return searchSrv.search({ query: '' }).then(res => {
+      return searchSrv.search({ query: "" }).then(res => {
         results = res;
       });
     });
 
-    it('should include recent dashboards section', () => {
-      expect(results[0].title).toBe('Recent Boards');
+    it("should include recent dashboards section", () => {
+      expect(results[0].title).toBe("Recent Boards");
     });
 
-    it('should return order decided by impressions store not api', () => {
-      expect(results[0].items[0].title).toBe('first but second');
-      expect(results[0].items[1].title).toBe('second but first');
+    it("should return order decided by impressions store not api", () => {
+      expect(results[0].items[0].title).toBe("first but second");
+      expect(results[0].items[1].title).toBe("second but first");
     });
 
-    describe('and 3 recent dashboards removed in backend', () => {
+    describe("and 3 recent dashboards removed in backend", () => {
       let results;
 
       beforeEach(() => {
         backendSrvMock.search = jest
           .fn()
           .mockReturnValueOnce(
-            Promise.resolve([{ id: 2, title: 'two' }, { id: 1, title: 'one' }]),
+            Promise.resolve([{ id: 2, title: "two" }, { id: 1, title: "one" }])
           )
           .mockReturnValue(Promise.resolve([]));
 
-        impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([4, 5, 1, 2, 3]);
+        impressionSrv.getDashboardOpened = jest
+          .fn()
+          .mockReturnValue([4, 5, 1, 2, 3]);
 
-        return searchSrv.search({ query: '' }).then(res => {
+        return searchSrv.search({ query: "" }).then(res => {
           results = res;
         });
       });
 
-      it('should return 2 dashboards', () => {
+      it("should return 2 dashboards", () => {
         expect(results[0].items.length).toBe(2);
         expect(results[0].items[0].id).toBe(1);
         expect(results[0].items[1].id).toBe(2);
@@ -81,60 +86,59 @@ describe('SearchSrv', () => {
     });
   });
 
-  describe('With starred dashboards', () => {
+  describe("With starred dashboards", () => {
     let results;
 
     beforeEach(() => {
       backendSrvMock.search = jest
         .fn()
-        .mockReturnValue(Promise.resolve([
-          {id: 1, title: 'starred'}
-        ]));
+        .mockReturnValue(Promise.resolve([{ id: 1, title: "starred" }]));
 
-      return searchSrv.search({ query: '' }).then(res => {
+      return searchSrv.search({ query: "" }).then(res => {
         results = res;
       });
     });
 
-    it('should include starred dashboards section', () => {
-      expect(results[0].title).toBe('Starred Boards');
+    it("should include starred dashboards section", () => {
+      expect(results[0].title).toBe("Starred Boards");
       expect(results[0].items.length).toBe(1);
     });
   });
 
-  describe('With starred dashboards and recent', () => {
+  describe("With starred dashboards and recent", () => {
     let results;
 
     beforeEach(() => {
       backendSrvMock.search = jest
         .fn()
-        .mockReturnValueOnce(Promise.resolve([
-          {id: 1, title: 'starred and recent', isStarred: true},
-          {id: 2, title: 'recent'}
-        ]))
-        .mockReturnValue(Promise.resolve([
-          {id: 1, title: 'starred and recent'}
-        ]));
-
-      impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1,2]);
-      return searchSrv.search({ query: '' }).then(res => {
+        .mockReturnValueOnce(
+          Promise.resolve([
+            { id: 1, title: "starred and recent", isStarred: true },
+            { id: 2, title: "recent" }
+          ])
+        )
+        .mockReturnValue(
+          Promise.resolve([{ id: 1, title: "starred and recent" }])
+        );
+
+      impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1, 2]);
+      return searchSrv.search({ query: "" }).then(res => {
         results = res;
       });
     });
 
-    it('should not show starred in recent', () => {
-      expect(results[1].title).toBe('Recent Boards');
-      expect(results[1].items[0].title).toBe('recent');
+    it("should not show starred in recent", () => {
+      expect(results[1].title).toBe("Recent Boards");
+      expect(results[1].items[0].title).toBe("recent");
     });
 
-    it('should show starred', () => {
-      expect(results[0].title).toBe('Starred Boards');
-      expect(results[0].items[0].title).toBe('starred and recent');
+    it("should show starred", () => {
+      expect(results[0].title).toBe("Starred Boards");
+      expect(results[0].items[0].title).toBe("starred and recent");
     });
-
   });
 
-  describe('with no query string and dashboards with folders returned', () => {
+  describe("with no query string and dashboards with folders returned", () => {
     let results;
 
     beforeEach(() => {
@@ -144,45 +148,45 @@ describe('SearchSrv', () => {
         .mockReturnValue(
           Promise.resolve([
             {
-              title: 'folder1',
-              type: 'dash-folder',
-              id: 1,
+              title: "folder1",
+              type: "dash-folder",
+              id: 1
             },
             {
-              title: 'dash with no folder',
-              type: 'dash-db',
-              id: 2,
+              title: "dash with no folder",
+              type: "dash-db",
+              id: 2
             },
             {
-              title: 'dash in folder1 1',
-              type: 'dash-db',
+              title: "dash in folder1 1",
+              type: "dash-db",
               id: 3,
-              folderId: 1,
+              folderId: 1
             },
             {
-              title: 'dash in folder1 2',
-              type: 'dash-db',
+              title: "dash in folder1 2",
+              type: "dash-db",
               id: 4,
-              folderId: 1,
-            },
-          ]),
+              folderId: 1
+            }
+          ])
         );
 
-      return searchSrv.search({ query: '' }).then(res => {
+      return searchSrv.search({ query: "" }).then(res => {
         results = res;
       });
     });
 
-    it('should create sections for each folder and root', () => {
+    it("should create sections for each folder and root", () => {
       expect(results).toHaveLength(2);
     });
 
-    it('should place folders first', () => {
-      expect(results[0].title).toBe('folder1');
+    it("should place folders first", () => {
+      expect(results[0].title).toBe("folder1");
     });
   });
 
-  describe('with query string and dashboards with folders returned', () => {
+  describe("with query string and dashboards with folders returned", () => {
     let results;
 
     beforeEach(() => {
@@ -192,47 +196,47 @@ describe('SearchSrv', () => {
         Promise.resolve([
           {
             id: 2,
-            title: 'dash with no folder',
-            type: 'dash-db',
+            title: "dash with no folder",
+            type: "dash-db"
           },
           {
             id: 3,
-            title: 'dash in folder1 1',
-            type: 'dash-db',
+            title: "dash in folder1 1",
+            type: "dash-db",
             folderId: 1,
-            folderTitle: 'folder1',
-          },
-        ]),
+            folderTitle: "folder1"
+          }
+        ])
       );
 
-      return searchSrv.search({ query: 'search' }).then(res => {
+      return searchSrv.search({ query: "search" }).then(res => {
         results = res;
       });
     });
 
-    it('should not specify folder ids', () => {
+    it("should not specify folder ids", () => {
       expect(backendSrvMock.search.mock.calls[0][0].folderIds).toHaveLength(0);
     });
 
-    it('should group results by folder', () => {
+    it("should group results by folder", () => {
       expect(results).toHaveLength(2);
     });
   });
 
-  describe('with tags', () => {
+  describe("with tags", () => {
     beforeEach(() => {
       backendSrvMock.search = jest.fn();
       backendSrvMock.search.mockReturnValue(Promise.resolve([]));
 
-      return searchSrv.search({ tag: ['atag'] }).then(() => {});
+      return searchSrv.search({ tag: ["atag"] }).then(() => {});
     });
 
-    it('should send tags query to backend search', () => {
+    it("should send tags query to backend search", () => {
       expect(backendSrvMock.search.mock.calls[0][0].tag).toHaveLength(1);
     });
   });
 
-  describe('with starred', () => {
+  describe("with starred", () => {
     beforeEach(() => {
       backendSrvMock.search = jest.fn();
       backendSrvMock.search.mockReturnValue(Promise.resolve([]));
@@ -240,12 +244,12 @@ describe('SearchSrv', () => {
       return searchSrv.search({ starred: true }).then(() => {});
     });
 
-    it('should send starred query to backend search', () => {
+    it("should send starred query to backend search", () => {
       expect(backendSrvMock.search.mock.calls[0][0].starred).toEqual(true);
     });
   });
 
-  describe('when skipping recent dashboards', () => {
+  describe("when skipping recent dashboards", () => {
     let getRecentDashboardsCalled = false;
 
     beforeEach(() => {
@@ -259,12 +263,12 @@ describe('SearchSrv', () => {
       return searchSrv.search({ skipRecent: true }).then(() => {});
     });
 
-    it('should not fetch recent dashboards', () => {
+    it("should not fetch recent dashboards", () => {
       expect(getRecentDashboardsCalled).toBeFalsy();
     });
   });
 
-  describe('when skipping starred dashboards', () => {
+  describe("when skipping starred dashboards", () => {
     let getStarredCalled = false;
 
     beforeEach(() => {
@@ -279,7 +283,7 @@ describe('SearchSrv', () => {
       return searchSrv.search({ skipStarred: true }).then(() => {});
     });
 
-    it('should not fetch starred dashboards', () => {
+    it("should not fetch starred dashboards", () => {
       expect(getStarredCalled).toBeFalsy();
     });
   });

+ 8 - 12
public/app/core/specs/store.jest.ts

@@ -1,4 +1,4 @@
-import store from '../store';
+import store from "../store";
 
 Object.assign(window, {
   localStorage: {
@@ -8,37 +8,33 @@ Object.assign(window, {
   }
 });
 
-describe('store', () => {
-
-  it("should store", ()=> {
+describe("store", () => {
+  it("should store", () => {
     store.set("key1", "123");
     expect(store.get("key1")).toBe("123");
   });
 
-  it("get key when undefined", ()=> {
+  it("get key when undefined", () => {
     expect(store.get("key2")).toBe(undefined);
   });
 
-  it("check if key exixts", ()=> {
+  it("check if key exixts", () => {
     store.set("key3", "123");
     expect(store.exists("key3")).toBe(true);
   });
 
-  it("get boolean when no key", ()=> {
+  it("get boolean when no key", () => {
     expect(store.getBool("key4", false)).toBe(false);
   });
 
-  it("get boolean", ()=> {
+  it("get boolean", () => {
     store.set("key5", "true");
     expect(store.getBool("key5", false)).toBe(true);
   });
 
-  it("key should be deleted", ()=> {
+  it("key should be deleted", () => {
     store.set("key6", "123");
     store.delete("key6");
     expect(store.exists("key6")).toBe(false);
   });
-
 });
-
-

+ 8 - 11
public/app/core/specs/table_model.jest.ts

@@ -1,9 +1,9 @@
-import TableModel from 'app/core/table_model';
+import TableModel from "app/core/table_model";
 
-describe('when sorting table desc', () => {
+describe("when sorting table desc", () => {
   var table;
   var panel = {
-    sort: {col: 0, desc: true},
+    sort: { col: 0, desc: true }
   };
 
   beforeEach(() => {
@@ -13,23 +13,22 @@ describe('when sorting table desc', () => {
     table.sort(panel.sort);
   });
 
-  it('should sort by time', () => {
+  it("should sort by time", () => {
     expect(table.rows[0][0]).toBe(105);
     expect(table.rows[1][0]).toBe(103);
     expect(table.rows[2][0]).toBe(100);
   });
 
-  it ('should mark column being sorted', () => {
+  it("should mark column being sorted", () => {
     expect(table.columns[0].sort).toBe(true);
     expect(table.columns[0].desc).toBe(true);
   });
-
 });
 
-describe('when sorting table asc', () => {
+describe("when sorting table asc", () => {
   var table;
   var panel = {
-    sort: {col: 1, desc: false},
+    sort: { col: 1, desc: false }
   };
 
   beforeEach(() => {
@@ -39,11 +38,9 @@ describe('when sorting table asc', () => {
     table.sort(panel.sort);
   });
 
-  it('should sort by time', () => {
+  it("should sort by time", () => {
     expect(table.rows[0][1]).toBe(10);
     expect(table.rows[1][1]).toBe(11);
     expect(table.rows[2][1]).toBe(15);
   });
-
 });
-

+ 107 - 97
public/app/core/specs/time_series.jest.ts

@@ -1,292 +1,303 @@
-import TimeSeries from 'app/core/time_series2';
+import TimeSeries from "app/core/time_series2";
 
 describe("TimeSeries", function() {
   var points, series;
-  var yAxisFormats = ['short', 'ms'];
+  var yAxisFormats = ["short", "ms"];
   var testData;
 
   beforeEach(function() {
     testData = {
-      alias: 'test',
-      datapoints: [
-        [1,2],[null,3],[10,4],[8,5]
-      ]
+      alias: "test",
+      datapoints: [[1, 2], [null, 3], [10, 4], [8, 5]]
     };
   });
 
-  describe('when getting flot pairs', function() {
-    it('with connected style, should ignore nulls', function() {
+  describe("when getting flot pairs", function() {
+    it("with connected style, should ignore nulls", function() {
       series = new TimeSeries(testData);
-      points = series.getFlotPairs('connected', yAxisFormats);
+      points = series.getFlotPairs("connected", yAxisFormats);
       expect(points.length).toBe(3);
     });
 
-    it('with null as zero style, should replace nulls with zero', function() {
+    it("with null as zero style, should replace nulls with zero", function() {
       series = new TimeSeries(testData);
-      points = series.getFlotPairs('null as zero', yAxisFormats);
+      points = series.getFlotPairs("null as zero", yAxisFormats);
       expect(points.length).toBe(4);
       expect(points[1][1]).toBe(0);
     });
 
-    it('if last is null current should pick next to last', function() {
+    it("if last is null current should pick next to last", function() {
       series = new TimeSeries({
-        datapoints: [[10,1], [null, 2]]
+        datapoints: [[10, 1], [null, 2]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.current).toBe(10);
     });
 
-    it('max value should work for negative values', function() {
+    it("max value should work for negative values", function() {
       series = new TimeSeries({
-        datapoints: [[-10,1], [-4, 2]]
+        datapoints: [[-10, 1], [-4, 2]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.max).toBe(-4);
     });
 
-    it('average value should ignore nulls', function() {
+    it("average value should ignore nulls", function() {
       series = new TimeSeries(testData);
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.avg).toBe(6.333333333333333);
     });
 
-    it('the delta value should account for nulls', function() {
+    it("the delta value should account for nulls", function() {
       series = new TimeSeries({
-        datapoints: [[1,2],[3,3],[null,4],[10,5],[15,6]]
+        datapoints: [[1, 2], [3, 3], [null, 4], [10, 5], [15, 6]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.delta).toBe(14);
     });
 
-    it('the delta value should account for nulls on first', function() {
+    it("the delta value should account for nulls on first", function() {
       series = new TimeSeries({
-        datapoints: [[null,2],[1,3],[10,4],[15,5]]
+        datapoints: [[null, 2], [1, 3], [10, 4], [15, 5]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.delta).toBe(14);
     });
 
-    it('the delta value should account for nulls on last', function() {
+    it("the delta value should account for nulls on last", function() {
       series = new TimeSeries({
-        datapoints: [[1,2],[5,3],[10,4],[null,5]]
+        datapoints: [[1, 2], [5, 3], [10, 4], [null, 5]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.delta).toBe(9);
     });
 
-    it('the delta value should account for resets', function() {
+    it("the delta value should account for resets", function() {
       series = new TimeSeries({
-        datapoints: [[1,2],[5,3],[10,4],[0,5],[10,6]]
+        datapoints: [[1, 2], [5, 3], [10, 4], [0, 5], [10, 6]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.delta).toBe(19);
     });
 
-    it('the delta value should account for resets on last', function() {
+    it("the delta value should account for resets on last", function() {
       series = new TimeSeries({
-        datapoints: [[1,2],[2,3],[10,4],[8,5]]
+        datapoints: [[1, 2], [2, 3], [10, 4], [8, 5]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.delta).toBe(17);
     });
 
-    it('the range value should be max - min', function() {
+    it("the range value should be max - min", function() {
       series = new TimeSeries(testData);
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.range).toBe(9);
     });
 
-    it('first value should ingone nulls', function() {
+    it("first value should ingone nulls", function() {
       series = new TimeSeries(testData);
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.first).toBe(1);
       series = new TimeSeries({
-        datapoints: [[null,2],[1,3],[10,4],[8,5]]
+        datapoints: [[null, 2], [1, 3], [10, 4], [8, 5]]
       });
-      series.getFlotPairs('null', yAxisFormats);
+      series.getFlotPairs("null", yAxisFormats);
       expect(series.stats.first).toBe(1);
     });
 
-    it('with null as zero style, average value should treat nulls as 0', function() {
+    it("with null as zero style, average value should treat nulls as 0", function() {
       series = new TimeSeries(testData);
-      series.getFlotPairs('null as zero', yAxisFormats);
+      series.getFlotPairs("null as zero", yAxisFormats);
       expect(series.stats.avg).toBe(4.75);
     });
 
-    it('average value should be null if all values is null', function() {
+    it("average value should be null if all values is null", function() {
       series = new TimeSeries({
-        datapoints: [[null,2],[null,3],[null,4],[null,5]]
+        datapoints: [[null, 2], [null, 3], [null, 4], [null, 5]]
       });
-      series.getFlotPairs('null');
+      series.getFlotPairs("null");
       expect(series.stats.avg).toBe(null);
     });
   });
 
-  describe('When checking if ms resolution is needed', function() {
-    describe('msResolution with second resolution timestamps', function() {
+  describe("When checking if ms resolution is needed", function() {
+    describe("msResolution with second resolution timestamps", function() {
       beforeEach(function() {
-        series = new TimeSeries({datapoints: [[45, 1234567890], [60, 1234567899]]});
+        series = new TimeSeries({
+          datapoints: [[45, 1234567890], [60, 1234567899]]
+        });
       });
 
-      it('should set hasMsResolution to false', function() {
+      it("should set hasMsResolution to false", function() {
         expect(series.hasMsResolution).toBe(false);
       });
     });
 
-    describe('msResolution with millisecond resolution timestamps', function() {
+    describe("msResolution with millisecond resolution timestamps", function() {
       beforeEach(function() {
-        series = new TimeSeries({datapoints: [[55, 1236547890001], [90, 1234456709000]]});
+        series = new TimeSeries({
+          datapoints: [[55, 1236547890001], [90, 1234456709000]]
+        });
       });
 
-      it('should show millisecond resolution tooltip', function() {
+      it("should show millisecond resolution tooltip", function() {
         expect(series.hasMsResolution).toBe(true);
       });
     });
 
-    describe('msResolution with millisecond resolution timestamps but with trailing zeroes', function() {
+    describe("msResolution with millisecond resolution timestamps but with trailing zeroes", function() {
       beforeEach(function() {
-        series = new TimeSeries({datapoints: [[45, 1234567890000], [60, 1234567899000]]});
+        series = new TimeSeries({
+          datapoints: [[45, 1234567890000], [60, 1234567899000]]
+        });
       });
 
-      it('should not show millisecond resolution tooltip', function() {
+      it("should not show millisecond resolution tooltip", function() {
         expect(series.hasMsResolution).toBe(false);
       });
     });
   });
 
-  describe('can detect if series contains ms precision', function() {
+  describe("can detect if series contains ms precision", function() {
     var fakedata;
 
     beforeEach(function() {
       fakedata = testData;
     });
 
-    it('missing datapoint with ms precision', function() {
+    it("missing datapoint with ms precision", function() {
       fakedata.datapoints[0] = [1337, 1234567890000];
       series = new TimeSeries(fakedata);
       expect(series.isMsResolutionNeeded()).toBe(false);
     });
 
-    it('contains datapoint with ms precision', function() {
+    it("contains datapoint with ms precision", function() {
       fakedata.datapoints[0] = [1337, 1236547890001];
       series = new TimeSeries(fakedata);
       expect(series.isMsResolutionNeeded()).toBe(true);
     });
   });
 
-  describe('series overrides', function() {
+  describe("series overrides", function() {
     var series;
     beforeEach(function() {
       series = new TimeSeries(testData);
     });
 
-    describe('fill & points', function() {
+    describe("fill & points", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', fill: 0, points: true }]);
+        series.alias = "test";
+        series.applySeriesOverrides([{ alias: "test", fill: 0, points: true }]);
       });
 
-      it('should set fill zero, and enable points', function() {
+      it("should set fill zero, and enable points", function() {
         expect(series.lines.fill).toBe(0.001);
         expect(series.points.show).toBe(true);
       });
     });
 
-    describe('series option overrides, bars, true & lines false', function() {
+    describe("series option overrides, bars, true & lines false", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', bars: true, lines: false }]);
+        series.alias = "test";
+        series.applySeriesOverrides([
+          { alias: "test", bars: true, lines: false }
+        ]);
       });
 
-      it('should disable lines, and enable bars', function() {
+      it("should disable lines, and enable bars", function() {
         expect(series.lines.show).toBe(false);
         expect(series.bars.show).toBe(true);
       });
     });
 
-    describe('series option overrides, linewidth, stack', function() {
+    describe("series option overrides, linewidth, stack", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', linewidth: 5, stack: false }]);
+        series.alias = "test";
+        series.applySeriesOverrides([
+          { alias: "test", linewidth: 5, stack: false }
+        ]);
       });
 
-      it('should disable stack, and set lineWidth', function() {
+      it("should disable stack, and set lineWidth", function() {
         expect(series.stack).toBe(false);
         expect(series.lines.lineWidth).toBe(5);
       });
     });
 
-    describe('series option overrides, dashes and lineWidth', function() {
+    describe("series option overrides, dashes and lineWidth", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', linewidth: 5, dashes: true }]);
+        series.alias = "test";
+        series.applySeriesOverrides([
+          { alias: "test", linewidth: 5, dashes: true }
+        ]);
       });
 
-      it('should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0', function() {
+      it("should enable dashes, set dashes lineWidth to 5 and lines lineWidth to 0", function() {
         expect(series.dashes.show).toBe(true);
         expect(series.dashes.lineWidth).toBe(5);
         expect(series.lines.lineWidth).toBe(0);
       });
     });
 
-    describe('series option overrides, fill below to', function() {
+    describe("series option overrides, fill below to", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', fillBelowTo: 'min' }]);
+        series.alias = "test";
+        series.applySeriesOverrides([{ alias: "test", fillBelowTo: "min" }]);
       });
 
-      it('should disable line fill and add fillBelowTo', function() {
-        expect(series.fillBelowTo).toBe('min');
+      it("should disable line fill and add fillBelowTo", function() {
+        expect(series.fillBelowTo).toBe("min");
       });
     });
 
-    describe('series option overrides, pointradius, steppedLine', function() {
+    describe("series option overrides, pointradius, steppedLine", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', pointradius: 5, steppedLine: true }]);
+        series.alias = "test";
+        series.applySeriesOverrides([
+          { alias: "test", pointradius: 5, steppedLine: true }
+        ]);
       });
 
-      it('should set pointradius, and set steppedLine', function() {
+      it("should set pointradius, and set steppedLine", function() {
         expect(series.points.radius).toBe(5);
         expect(series.lines.steps).toBe(true);
       });
     });
 
-    describe('override match on regex', function() {
+    describe("override match on regex", function() {
       beforeEach(function() {
-        series.alias = 'test_01';
-        series.applySeriesOverrides([{ alias: '/.*01/', lines: false }]);
+        series.alias = "test_01";
+        series.applySeriesOverrides([{ alias: "/.*01/", lines: false }]);
       });
 
-      it('should match second series', function() {
+      it("should match second series", function() {
         expect(series.lines.show).toBe(false);
       });
     });
 
-    describe('override series y-axis, and z-index', function() {
+    describe("override series y-axis, and z-index", function() {
       beforeEach(function() {
-        series.alias = 'test';
-        series.applySeriesOverrides([{ alias: 'test', yaxis: 2, zindex: 2 }]);
+        series.alias = "test";
+        series.applySeriesOverrides([{ alias: "test", yaxis: 2, zindex: 2 }]);
       });
 
-      it('should set yaxis', function() {
+      it("should set yaxis", function() {
         expect(series.yaxis).toBe(2);
       });
 
-      it('should set zindex', function() {
+      it("should set zindex", function() {
         expect(series.zindex).toBe(2);
       });
     });
-
   });
 
-  describe('value formatter', function() {
+  describe("value formatter", function() {
     var series;
     beforeEach(function() {
       series = new TimeSeries(testData);
     });
 
-    it('should format non-numeric values as empty string', function() {
+    it("should format non-numeric values as empty string", function() {
       expect(series.formatValue(null)).toBe("");
       expect(series.formatValue(undefined)).toBe("");
       expect(series.formatValue(NaN)).toBe("");
@@ -294,5 +305,4 @@ describe("TimeSeries", function() {
       expect(series.formatValue(-Infinity)).toBe("");
     });
   });
-
 });

+ 46 - 35
public/app/core/specs/value_select_dropdown_specs.ts

@@ -1,5 +1,12 @@
-import {describe, beforeEach, it, expect, angularMocks, sinon} from 'test/lib/common';
-import 'app/core/directives/value_select_dropdown';
+import {
+  describe,
+  beforeEach,
+  it,
+  expect,
+  angularMocks,
+  sinon
+} from "test/lib/common";
+import "app/core/directives/value_select_dropdown";
 
 describe("SelectDropdownCtrl", function() {
   var scope;
@@ -8,23 +15,25 @@ describe("SelectDropdownCtrl", function() {
   var rootScope;
   var q;
 
-  beforeEach(angularMocks.module('grafana.core'));
-  beforeEach(angularMocks.inject(function($controller, $rootScope, $q, $httpBackend) {
-    rootScope = $rootScope;
-    q = $q;
-    scope = $rootScope.$new();
-    ctrl = $controller('ValueSelectDropdownCtrl', {$scope: scope});
-    ctrl.onUpdated = sinon.spy();
-    $httpBackend.when('GET', /\.html$/).respond('');
-  }));
+  beforeEach(angularMocks.module("grafana.core"));
+  beforeEach(
+    angularMocks.inject(function($controller, $rootScope, $q, $httpBackend) {
+      rootScope = $rootScope;
+      q = $q;
+      scope = $rootScope.$new();
+      ctrl = $controller("ValueSelectDropdownCtrl", { $scope: scope });
+      ctrl.onUpdated = sinon.spy();
+      $httpBackend.when("GET", /\.html$/).respond("");
+    })
+  );
 
   describe("Given simple variable", function() {
     beforeEach(function() {
       ctrl.variable = {
-        current: {text: 'hej', value: 'hej' },
+        current: { text: "hej", value: "hej" },
         getValuesForTag: function(key) {
           return q.when(tagValuesMap[key]);
-        },
+        }
       };
       ctrl.init();
     });
@@ -37,11 +46,11 @@ describe("SelectDropdownCtrl", function() {
   describe("Given variable with tags and dropdown is opened", function() {
     beforeEach(function() {
       ctrl.variable = {
-        current: {text: 'server-1', value: 'server-1'},
+        current: { text: "server-1", value: "server-1" },
         options: [
-          {text: 'server-1', value: 'server-1', selected: true},
-          {text: 'server-2', value: 'server-2'},
-          {text: 'server-3', value: 'server-3'},
+          { text: "server-1", value: "server-1", selected: true },
+          { text: "server-2", value: "server-2" },
+          { text: "server-3", value: "server-3" }
         ],
         tags: ["key1", "key2", "key3"],
         getValuesForTag: function(key) {
@@ -49,9 +58,9 @@ describe("SelectDropdownCtrl", function() {
         },
         multi: true
       };
-      tagValuesMap.key1 = ['server-1', 'server-3'];
-      tagValuesMap.key2 = ['server-2', 'server-3'];
-      tagValuesMap.key3 = ['server-1', 'server-2', 'server-3'];
+      tagValuesMap.key1 = ["server-1", "server-3"];
+      tagValuesMap.key2 = ["server-2", "server-3"];
+      tagValuesMap.key3 = ["server-1", "server-2", "server-3"];
       ctrl.init();
       ctrl.show();
     });
@@ -70,21 +79,21 @@ describe("SelectDropdownCtrl", function() {
     });
 
     it("should set linkText", function() {
-      expect(ctrl.linkText).to.be('server-1');
+      expect(ctrl.linkText).to.be("server-1");
     });
 
-    describe('after adititional value is selected', function() {
+    describe("after adititional value is selected", function() {
       beforeEach(function() {
         ctrl.selectValue(ctrl.options[2], {});
         ctrl.commitChanges();
       });
 
-      it('should update link text', function() {
-        expect(ctrl.linkText).to.be('server-1 + server-3');
+      it("should update link text", function() {
+        expect(ctrl.linkText).to.be("server-1 + server-3");
       });
     });
 
-    describe('When tag is selected', function() {
+    describe("When tag is selected", function() {
       beforeEach(function() {
         ctrl.selectTag(ctrl.tags[0]);
         rootScope.$digest();
@@ -101,10 +110,10 @@ describe("SelectDropdownCtrl", function() {
       });
 
       it("link text should not include tag values", function() {
-        expect(ctrl.linkText).to.be('');
+        expect(ctrl.linkText).to.be("");
       });
 
-      describe('and then dropdown is opened and closed without changes', function() {
+      describe("and then dropdown is opened and closed without changes", function() {
         beforeEach(function() {
           ctrl.show();
           ctrl.commitChanges();
@@ -116,7 +125,7 @@ describe("SelectDropdownCtrl", function() {
         });
       });
 
-      describe('and then unselected', function() {
+      describe("and then unselected", function() {
         beforeEach(function() {
           ctrl.selectTag(ctrl.tags[0]);
           rootScope.$digest();
@@ -127,7 +136,7 @@ describe("SelectDropdownCtrl", function() {
         });
       });
 
-      describe('and then value is unselected', function() {
+      describe("and then value is unselected", function() {
         beforeEach(function() {
           ctrl.selectValue(ctrl.options[0], {});
         });
@@ -142,11 +151,15 @@ describe("SelectDropdownCtrl", function() {
   describe("Given variable with selected tags", function() {
     beforeEach(function() {
       ctrl.variable = {
-        current: {text: 'server-1', value: 'server-1', tags: [{text: 'key1', selected: true}] },
+        current: {
+          text: "server-1",
+          value: "server-1",
+          tags: [{ text: "key1", selected: true }]
+        },
         options: [
-          {text: 'server-1', value: 'server-1'},
-          {text: 'server-2', value: 'server-2'},
-          {text: 'server-3', value: 'server-3'},
+          { text: "server-1", value: "server-1" },
+          { text: "server-2", value: "server-2" },
+          { text: "server-3", value: "server-3" }
         ],
         tags: ["key1", "key2", "key3"],
         getValuesForTag: function(key) {
@@ -161,7 +174,5 @@ describe("SelectDropdownCtrl", function() {
     it("should set tag as selected", function() {
       expect(ctrl.tags[0].selected).to.be(true);
     });
-
   });
 });
-

+ 1 - 3
public/app/core/store.ts

@@ -1,5 +1,4 @@
 export class Store {
-
   get(key) {
     return window.localStorage[key];
   }
@@ -12,7 +11,7 @@ export class Store {
     if (def !== void 0 && !this.exists(key)) {
       return def;
     }
-    return window.localStorage[key] === 'true';
+    return window.localStorage[key] === "true";
   }
 
   exists(key) {
@@ -22,7 +21,6 @@ export class Store {
   delete(key) {
     window.localStorage.removeItem(key);
   }
-
 }
 
 const store = new Store();

+ 1 - 2
public/app/core/table_model.ts

@@ -1,4 +1,3 @@
-
 export default class TableModel {
   columns: any[];
   rows: any[];
@@ -9,7 +8,7 @@ export default class TableModel {
     this.columns = [];
     this.columnMap = {};
     this.rows = [];
-    this.type = 'table';
+    this.type = "table";
   }
 
   sort(options) {

+ 81 - 41
public/app/core/time_series2.ts

@@ -1,11 +1,13 @@
-import kbn from 'app/core/utils/kbn';
-import {getFlotTickDecimals} from 'app/core/utils/ticks';
-import _ from 'lodash';
+import kbn from "app/core/utils/kbn";
+import { getFlotTickDecimals } from "app/core/utils/ticks";
+import _ from "lodash";
 
 function matchSeriesOverride(aliasOrRegex, seriesAlias) {
-  if (!aliasOrRegex) { return false; }
+  if (!aliasOrRegex) {
+    return false;
+  }
 
-  if (aliasOrRegex[0] === '/') {
+  if (aliasOrRegex[0] === "/") {
     var regex = kbn.stringToJsRegex(aliasOrRegex);
     return seriesAlias.match(regex) != null;
   }
@@ -14,7 +16,7 @@ function matchSeriesOverride(aliasOrRegex, seriesAlias) {
 }
 
 function translateFillOption(fill) {
-  return fill === 0 ? 0.001 : fill/10;
+  return fill === 0 ? 0.001 : fill / 10;
 }
 
 /**
@@ -27,7 +29,7 @@ export function updateLegendValues(data: TimeSeries[], panel) {
     let series = data[i];
     let yaxes = panel.yaxes;
     let axis = yaxes[series.yaxis - 1];
-    let {tickDecimals, scaledDecimals} = getFlotTickDecimals(data, axis);
+    let { tickDecimals, scaledDecimals } = getFlotTickDecimals(data, axis);
     let formater = kbn.valueFormats[panel.yaxes[series.yaxis - 1].format];
 
     // decimal override
@@ -56,7 +58,7 @@ export function getDataMinMax(data: TimeSeries[]) {
     }
   }
 
-  return {datamin, datamax};
+  return { datamin, datamax };
 }
 
 export default class TimeSeries {
@@ -120,29 +122,59 @@ export default class TimeSeries {
       if (!matchSeriesOverride(override.alias, this.alias)) {
         continue;
       }
-      if (override.lines !== void 0) { this.lines.show = override.lines; }
+      if (override.lines !== void 0) {
+        this.lines.show = override.lines;
+      }
       if (override.dashes !== void 0) {
-         this.dashes.show = override.dashes;
-         this.lines.lineWidth = 0;
+        this.dashes.show = override.dashes;
+        this.lines.lineWidth = 0;
+      }
+      if (override.points !== void 0) {
+        this.points.show = override.points;
+      }
+      if (override.bars !== void 0) {
+        this.bars.show = override.bars;
+      }
+      if (override.fill !== void 0) {
+        this.lines.fill = translateFillOption(override.fill);
+      }
+      if (override.stack !== void 0) {
+        this.stack = override.stack;
       }
-      if (override.points !== void 0) { this.points.show = override.points; }
-      if (override.bars !== void 0) { this.bars.show = override.bars; }
-      if (override.fill !== void 0) { this.lines.fill = translateFillOption(override.fill); }
-      if (override.stack !== void 0) { this.stack = override.stack; }
       if (override.linewidth !== void 0) {
-         this.lines.lineWidth = this.dashes.show ? 0: override.linewidth;
-         this.dashes.lineWidth = override.linewidth;
+        this.lines.lineWidth = this.dashes.show ? 0 : override.linewidth;
+        this.dashes.lineWidth = override.linewidth;
+      }
+      if (override.dashLength !== void 0) {
+        this.dashes.dashLength[0] = override.dashLength;
+      }
+      if (override.spaceLength !== void 0) {
+        this.dashes.dashLength[1] = override.spaceLength;
+      }
+      if (override.nullPointMode !== void 0) {
+        this.nullPointMode = override.nullPointMode;
+      }
+      if (override.pointradius !== void 0) {
+        this.points.radius = override.pointradius;
+      }
+      if (override.steppedLine !== void 0) {
+        this.lines.steps = override.steppedLine;
+      }
+      if (override.zindex !== void 0) {
+        this.zindex = override.zindex;
+      }
+      if (override.fillBelowTo !== void 0) {
+        this.fillBelowTo = override.fillBelowTo;
+      }
+      if (override.color !== void 0) {
+        this.color = override.color;
+      }
+      if (override.transform !== void 0) {
+        this.transform = override.transform;
+      }
+      if (override.legend !== void 0) {
+        this.legend = override.legend;
       }
-      if (override.dashLength !== void 0) { this.dashes.dashLength[0] = override.dashLength; }
-      if (override.spaceLength !== void 0) { this.dashes.dashLength[1] = override.spaceLength; }
-      if (override.nullPointMode !== void 0) { this.nullPointMode = override.nullPointMode; }
-      if (override.pointradius !== void 0) { this.points.radius = override.pointradius; }
-      if (override.steppedLine !== void 0) { this.lines.steps = override.steppedLine; }
-      if (override.zindex !== void 0) { this.zindex = override.zindex; }
-      if (override.fillBelowTo !== void 0) { this.fillBelowTo = override.fillBelowTo; }
-      if (override.color !== void 0) { this.color = override.color; }
-      if (override.transform !== void 0) { this.transform = override.transform; }
-      if (override.legend !== void 0) { this.legend = override.legend; }
 
       if (override.yaxis !== void 0) {
         this.yaxis = override.yaxis;
@@ -167,8 +199,8 @@ export default class TimeSeries {
     this.allIsNull = true;
     this.allIsZero = true;
 
-    var ignoreNulls = fillStyle === 'connected';
-    var nullAsZero = fillStyle === 'null as zero';
+    var ignoreNulls = fillStyle === "connected";
+    var nullAsZero = fillStyle === "null as zero";
     var currentTime;
     var currentValue;
     var nonNulls = 0;
@@ -191,7 +223,9 @@ export default class TimeSeries {
       previousTime = currentTime;
 
       if (currentValue === null) {
-        if (ignoreNulls) { continue; }
+        if (ignoreNulls) {
+          continue;
+        }
         if (nullAsZero) {
           currentValue = 0;
         }
@@ -215,16 +249,18 @@ export default class TimeSeries {
         if (this.stats.first === null) {
           this.stats.first = currentValue;
         } else {
-          if (previousValue > currentValue) {   // counter reset
+          if (previousValue > currentValue) {
+            // counter reset
             previousDeltaUp = false;
-            if (i === this.datapoints.length-1) {  // reset on last
-                this.stats.delta += currentValue;
+            if (i === this.datapoints.length - 1) {
+              // reset on last
+              this.stats.delta += currentValue;
             }
           } else {
             if (previousDeltaUp) {
-              this.stats.delta += currentValue - previousValue;    // normal increment
+              this.stats.delta += currentValue - previousValue; // normal increment
             } else {
-              this.stats.delta += currentValue;   // account for counter reset
+              this.stats.delta += currentValue; // account for counter reset
             }
             previousDeltaUp = true;
           }
@@ -243,14 +279,18 @@ export default class TimeSeries {
       result.push([currentTime, currentValue]);
     }
 
-    if (this.stats.max === -Number.MAX_VALUE) { this.stats.max = null; }
-    if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; }
+    if (this.stats.max === -Number.MAX_VALUE) {
+      this.stats.max = null;
+    }
+    if (this.stats.min === Number.MAX_VALUE) {
+      this.stats.min = null;
+    }
 
     if (result.length && !this.allIsNull) {
-      this.stats.avg = (this.stats.total / nonNulls);
-      this.stats.current = result[result.length-1][1];
+      this.stats.avg = this.stats.total / nonNulls;
+      this.stats.current = result[result.length - 1][1];
       if (this.stats.current === null && result.length > 1) {
-        this.stats.current = result[result.length-2][1];
+        this.stats.current = result[result.length - 2][1];
       }
     }
     if (this.stats.max !== null && this.stats.min !== null) {
@@ -281,7 +321,7 @@ export default class TimeSeries {
     for (var i = 0; i < this.datapoints.length; i++) {
       if (this.datapoints[i][1] !== null) {
         var timestamp = this.datapoints[i][1].toString();
-        if (timestamp.length === 13 && (timestamp % 1000) !== 0) {
+        if (timestamp.length === 13 && timestamp % 1000 !== 0) {
           return true;
         }
       }

+ 61 - 12
public/app/core/utils/colors.ts

@@ -1,31 +1,80 @@
-import _ from 'lodash';
-import tinycolor from 'tinycolor2';
+import _ from "lodash";
+import tinycolor from "tinycolor2";
 
 export const PALETTE_ROWS = 4;
 export const PALETTE_COLUMNS = 14;
-export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)';
+export const DEFAULT_ANNOTATION_COLOR = "rgba(0, 211, 255, 1)";
 export const OK_COLOR = "rgba(11, 237, 50, 1)";
 export const ALERTING_COLOR = "rgba(237, 46, 24, 1)";
 export const NO_DATA_COLOR = "rgba(150, 150, 150, 1)";
 export const REGION_FILL_ALPHA = 0.09;
 
 let colors = [
-  "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
-  "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
-  "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
-  "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
-  "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
-  "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
-  "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
+  "#7EB26D",
+  "#EAB839",
+  "#6ED0E0",
+  "#EF843C",
+  "#E24D42",
+  "#1F78C1",
+  "#BA43A9",
+  "#705DA0",
+  "#508642",
+  "#CCA300",
+  "#447EBC",
+  "#C15C17",
+  "#890F02",
+  "#0A437C",
+  "#6D1F62",
+  "#584477",
+  "#B7DBAB",
+  "#F4D598",
+  "#70DBED",
+  "#F9BA8F",
+  "#F29191",
+  "#82B5D8",
+  "#E5A8E2",
+  "#AEA2E0",
+  "#629E51",
+  "#E5AC0E",
+  "#64B0C8",
+  "#E0752D",
+  "#BF1B00",
+  "#0A50A1",
+  "#962D82",
+  "#614D93",
+  "#9AC48A",
+  "#F2C96D",
+  "#65C5DB",
+  "#F9934E",
+  "#EA6460",
+  "#5195CE",
+  "#D683CE",
+  "#806EB7",
+  "#3F6833",
+  "#967302",
+  "#2F575E",
+  "#99440A",
+  "#58140C",
+  "#052B51",
+  "#511749",
+  "#3F2B5B",
+  "#E0F9D7",
+  "#FCEACA",
+  "#CFFAFF",
+  "#F9E2D2",
+  "#FCE2DE",
+  "#BADFF4",
+  "#F9D9F9",
+  "#DEDAF7"
 ];
 
 export function sortColorsByHue(hexColors) {
   let hslColors = _.map(hexColors, hexToHsl);
 
-  let sortedHSLColors = _.sortBy(hslColors, ['h']);
+  let sortedHSLColors = _.sortBy(hslColors, ["h"]);
   sortedHSLColors = _.chunk(sortedHSLColors, PALETTE_ROWS);
   sortedHSLColors = _.map(sortedHSLColors, chunk => {
-    return _.sortBy(chunk, 'l');
+    return _.sortBy(chunk, "l");
   });
   sortedHSLColors = _.flattenDeep(_.zip(...sortedHSLColors));
 

+ 14 - 13
public/app/core/utils/css_loader.ts

@@ -1,10 +1,10 @@
 ///<reference path="../../headers/common.d.ts" />
 
 var waitSeconds = 100;
-var head = document.getElementsByTagName('head')[0];
+var head = document.getElementsByTagName("head")[0];
 
 // get all link tags in the page
-var links = document.getElementsByTagName('link');
+var links = document.getElementsByTagName("link");
 var linkHrefs = [];
 for (var i = 0; i < links.length; i++) {
   linkHrefs.push(links[i].href);
@@ -27,9 +27,9 @@ var noop = function() {};
 
 var loadCSS = function(url) {
   return new Promise(function(resolve, reject) {
-    var link = document.createElement('link');
+    var link = document.createElement("link");
     var timeout = setTimeout(function() {
-      reject('Unable to load CSS');
+      reject("Unable to load CSS");
     }, waitSeconds * 1000);
 
     var _callback = function(error) {
@@ -39,23 +39,25 @@ var loadCSS = function(url) {
         if (error) {
           reject(error);
         } else {
-          resolve('');
+          resolve("");
         }
       }, 7);
     };
 
-    link.type = 'text/css';
-    link.rel = 'stylesheet';
+    link.type = "text/css";
+    link.rel = "stylesheet";
     link.href = url;
 
     if (!isWebkit) {
-      link.onload = function() { _callback(undefined); };
+      link.onload = function() {
+        _callback(undefined);
+      };
     } else {
       webkitLoadCheck(link, _callback);
     }
 
     link.onerror = function(evt: any) {
-      _callback(evt.error || new Error('Error loading CSS file.'));
+      _callback(evt.error || new Error("Error loading CSS file."));
     };
 
     head.appendChild(link);
@@ -63,16 +65,15 @@ var loadCSS = function(url) {
 };
 
 export function fetch(load): any {
-  if (typeof window === 'undefined') {
-    return '';
+  if (typeof window === "undefined") {
+    return "";
   }
 
   // dont reload styles loaded in the head
   for (var i = 0; i < linkHrefs.length; i++) {
     if (load.address === linkHrefs[i]) {
-      return '';
+      return "";
     }
   }
   return loadCSS(load.address);
 }
-

+ 24 - 17
public/app/core/utils/datemath.ts

@@ -1,32 +1,38 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import moment from 'moment';
+import _ from "lodash";
+import moment from "moment";
 
-var units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
+var units = ["y", "M", "w", "d", "h", "m", "s"];
 
 export function parse(text, roundUp?, timezone?) {
-  if (!text) { return undefined; }
-  if (moment.isMoment(text)) { return text; }
-  if (_.isDate(text)) { return moment(text); }
+  if (!text) {
+    return undefined;
+  }
+  if (moment.isMoment(text)) {
+    return text;
+  }
+  if (_.isDate(text)) {
+    return moment(text);
+  }
 
   var time;
-  var mathString = '';
+  var mathString = "";
   var index;
   var parseString;
 
-  if (text.substring(0, 3) === 'now') {
-    if (timezone === 'utc') {
+  if (text.substring(0, 3) === "now") {
+    if (timezone === "utc") {
       time = moment.utc();
     } else {
       time = moment();
     }
-    mathString = text.substring('now'.length);
+    mathString = text.substring("now".length);
   } else {
-    index = text.indexOf('||');
+    index = text.indexOf("||");
     if (index === -1) {
       parseString = text;
-      mathString = ''; // nothing else
+      mathString = ""; // nothing else
     } else {
       parseString = text.substring(0, index);
       mathString = text.substring(index + 2);
@@ -66,11 +72,11 @@ export function parseDateMath(mathString, time, roundUp?) {
     var num;
     var unit;
 
-    if (c === '/') {
+    if (c === "/") {
       type = 0;
-    } else if (c === '+') {
+    } else if (c === "+") {
       type = 1;
-    } else if (c === '-') {
+    } else if (c === "-") {
       type = 2;
     } else {
       return undefined;
@@ -84,7 +90,9 @@ export function parseDateMath(mathString, time, roundUp?) {
       var numFrom = i;
       while (!isNaN(mathString.charAt(i))) {
         i++;
-        if (i > 10) { return undefined; }
+        if (i > 10) {
+          return undefined;
+        }
       }
       num = parseInt(mathString.substring(numFrom, i), 10);
     }
@@ -115,4 +123,3 @@ export function parseDateMath(mathString, time, roundUp?) {
   }
   return dateTime;
 }
-

+ 69 - 55
public/app/core/utils/file_export.ts

@@ -1,72 +1,86 @@
-import _ from 'lodash';
-import moment from 'moment';
-import {saveAs} from 'file-saver';
+import _ from "lodash";
+import moment from "moment";
+import { saveAs } from "file-saver";
 
-const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
+const DEFAULT_DATETIME_FORMAT = "YYYY-MM-DDTHH:mm:ssZ";
 
-export function exportSeriesListToCsv(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
-    var text = (excel ? 'sep=;\n' : '') + 'Series;Time;Value\n';
-    _.each(seriesList, function(series) {
-        _.each(series.datapoints, function(dp) {
-            text += series.alias + ';' + moment(dp[1]).format(dateTimeFormat) + ';' + dp[0] + '\n';
-        });
+export function exportSeriesListToCsv(
+  seriesList,
+  dateTimeFormat = DEFAULT_DATETIME_FORMAT,
+  excel = false
+) {
+  var text = (excel ? "sep=;\n" : "") + "Series;Time;Value\n";
+  _.each(seriesList, function(series) {
+    _.each(series.datapoints, function(dp) {
+      text +=
+        series.alias +
+        ";" +
+        moment(dp[1]).format(dateTimeFormat) +
+        ";" +
+        dp[0] +
+        "\n";
     });
-    saveSaveBlob(text, 'grafana_data_export.csv');
+  });
+  saveSaveBlob(text, "grafana_data_export.csv");
 }
 
-export function exportSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
-    var text = (excel ? 'sep=;\n' : '') + 'Time;';
-    // add header
-    _.each(seriesList, function(series) {
-        text += series.alias + ';';
-    });
-    text = text.substring(0,text.length-1);
-    text += '\n';
+export function exportSeriesListToCsvColumns(
+  seriesList,
+  dateTimeFormat = DEFAULT_DATETIME_FORMAT,
+  excel = false
+) {
+  var text = (excel ? "sep=;\n" : "") + "Time;";
+  // add header
+  _.each(seriesList, function(series) {
+    text += series.alias + ";";
+  });
+  text = text.substring(0, text.length - 1);
+  text += "\n";
 
-    // process data
-    var dataArr = [[]];
-    var sIndex = 1;
-    _.each(seriesList, function(series) {
-        var cIndex = 0;
-        dataArr.push([]);
-        _.each(series.datapoints, function(dp) {
-            dataArr[0][cIndex] = moment(dp[1]).format(dateTimeFormat);
-            dataArr[sIndex][cIndex] = dp[0];
-            cIndex++;
-        });
-        sIndex++;
+  // process data
+  var dataArr = [[]];
+  var sIndex = 1;
+  _.each(seriesList, function(series) {
+    var cIndex = 0;
+    dataArr.push([]);
+    _.each(series.datapoints, function(dp) {
+      dataArr[0][cIndex] = moment(dp[1]).format(dateTimeFormat);
+      dataArr[sIndex][cIndex] = dp[0];
+      cIndex++;
     });
+    sIndex++;
+  });
 
-    // make text
-    for (var i = 0; i < dataArr[0].length; i++) {
-        text += dataArr[0][i] + ';';
-        for (var j = 1; j < dataArr.length; j++) {
-            text += dataArr[j][i] + ';';
-        }
-        text = text.substring(0,text.length-1);
-        text += '\n';
+  // make text
+  for (var i = 0; i < dataArr[0].length; i++) {
+    text += dataArr[0][i] + ";";
+    for (var j = 1; j < dataArr.length; j++) {
+      text += dataArr[j][i] + ";";
     }
-    saveSaveBlob(text, 'grafana_data_export.csv');
+    text = text.substring(0, text.length - 1);
+    text += "\n";
+  }
+  saveSaveBlob(text, "grafana_data_export.csv");
 }
 
 export function exportTableDataToCsv(table, excel = false) {
-  var text = excel ? 'sep=;\n' : '';
-    // add header
-    _.each(table.columns, function(column) {
-        text += (column.title || column.text) + ';';
-    });
-    text += '\n';
-    // process data
-    _.each(table.rows, function(row) {
-        _.each(row, function(value) {
-            text += value + ';';
-        });
-        text += '\n';
+  var text = excel ? "sep=;\n" : "";
+  // add header
+  _.each(table.columns, function(column) {
+    text += (column.title || column.text) + ";";
+  });
+  text += "\n";
+  // process data
+  _.each(table.rows, function(row) {
+    _.each(row, function(value) {
+      text += value + ";";
     });
-    saveSaveBlob(text, 'grafana_data_export.csv');
+    text += "\n";
+  });
+  saveSaveBlob(text, "grafana_data_export.csv");
 }
 
 export function saveSaveBlob(payload, fname) {
-    var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
-    saveAs(blob, fname);
+  var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
+  saveAs(blob, fname);
 }

+ 7 - 2
public/app/core/utils/flatten.ts

@@ -4,7 +4,7 @@
 export default function flatten(target, opts): any {
   opts = opts || {};
 
-  var delimiter = opts.delimiter || '.';
+  var delimiter = opts.delimiter || ".";
   var maxDepth = opts.maxDepth || 3;
   var currentDepth = 1;
   var output = {};
@@ -22,7 +22,12 @@ export default function flatten(target, opts): any {
         maxDepth = currentDepth + 1;
       }
 
-      if (!isarray && isobject && Object.keys(value).length && currentDepth < maxDepth) {
+      if (
+        !isarray &&
+        isobject &&
+        Object.keys(value).length &&
+        currentDepth < maxDepth
+      ) {
         ++currentDepth;
         return step(value, newKey);
       }

+ 493 - 392
public/app/core/utils/kbn.ts

@@ -1,12 +1,12 @@
-import _ from 'lodash';
-import moment from 'moment';
+import _ from "lodash";
+import moment from "moment";
 
 var kbn: any = {};
 
 kbn.valueFormats = {};
 
 kbn.regexEscape = function(value) {
-  return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&');
+  return value.replace(/[\\^$*+?.()|[\]{}\/]/g, "\\$&");
 };
 
 ///// HELPER FUNCTIONS /////
@@ -105,41 +105,41 @@ kbn.round_interval = function(interval) {
 kbn.secondsToHms = function(seconds) {
   var numyears = Math.floor(seconds / 31536000);
   if (numyears) {
-    return numyears + 'y';
+    return numyears + "y";
   }
   var numdays = Math.floor((seconds % 31536000) / 86400);
   if (numdays) {
-    return numdays + 'd';
+    return numdays + "d";
   }
   var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
   if (numhours) {
-    return numhours + 'h';
+    return numhours + "h";
   }
   var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
   if (numminutes) {
-    return numminutes + 'm';
+    return numminutes + "m";
   }
   var numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60);
   if (numseconds) {
-    return numseconds + 's';
+    return numseconds + "s";
   }
   var nummilliseconds = Math.floor(seconds * 1000.0);
   if (nummilliseconds) {
-    return nummilliseconds + 'ms';
+    return nummilliseconds + "ms";
   }
 
-  return 'less than a millisecond'; //'just now' //or other string you like;
+  return "less than a millisecond"; //'just now' //or other string you like;
 };
 
 kbn.to_percent = function(nr, outof) {
-  return Math.floor(nr / outof * 10000) / 100 + '%';
+  return Math.floor(nr / outof * 10000) / 100 + "%";
 };
 
 kbn.addslashes = function(str) {
-  str = str.replace(/\\/g, '\\\\');
+  str = str.replace(/\\/g, "\\\\");
   str = str.replace(/\'/g, "\\'");
   str = str.replace(/\"/g, '\\"');
-  str = str.replace(/\0/g, '\\0');
+  str = str.replace(/\0/g, "\\0");
   return str;
 };
 
@@ -154,7 +154,7 @@ kbn.intervals_in_seconds = {
   h: 3600,
   m: 60,
   s: 1,
-  ms: 0.001,
+  ms: 0.001
 };
 
 kbn.calculateInterval = function(range, resolution, lowLimitInterval) {
@@ -162,32 +162,36 @@ kbn.calculateInterval = function(range, resolution, lowLimitInterval) {
   var intervalMs;
 
   if (lowLimitInterval) {
-    if (lowLimitInterval[0] === '>') {
+    if (lowLimitInterval[0] === ">") {
       lowLimitInterval = lowLimitInterval.slice(1);
     }
     lowLimitMs = kbn.interval_to_ms(lowLimitInterval);
   }
 
-  intervalMs = kbn.round_interval((range.to.valueOf() - range.from.valueOf()) / resolution);
+  intervalMs = kbn.round_interval(
+    (range.to.valueOf() - range.from.valueOf()) / resolution
+  );
   if (lowLimitMs > intervalMs) {
     intervalMs = lowLimitMs;
   }
 
   return {
     intervalMs: intervalMs,
-    interval: kbn.secondsToHms(intervalMs / 1000),
+    interval: kbn.secondsToHms(intervalMs / 1000)
   };
 };
 
 kbn.describe_interval = function(str) {
   var matches = str.match(kbn.interval_regex);
   if (!matches || !_.has(kbn.intervals_in_seconds, matches[2])) {
-    throw new Error('Invalid interval string, expecting a number followed by one of "Mwdhmsy"');
+    throw new Error(
+      'Invalid interval string, expecting a number followed by one of "Mwdhmsy"'
+    );
   } else {
     return {
       sec: kbn.intervals_in_seconds[matches[2]],
       type: matches[2],
-      count: parseInt(matches[1], 10),
+      count: parseInt(matches[1], 10)
     };
   }
 };
@@ -205,7 +209,11 @@ kbn.interval_to_seconds = function(str) {
 kbn.query_color_dot = function(color, diameter) {
   return (
     '<div class="icon-circle" style="' +
-    ['display:inline-block', 'color:' + color, 'font-size:' + diameter + 'px'].join(';') +
+    [
+      "display:inline-block",
+      "color:" + color,
+      "font-size:" + diameter + "px"
+    ].join(";") +
     '"></div>'
   );
 };
@@ -213,46 +221,55 @@ kbn.query_color_dot = function(color, diameter) {
 kbn.slugifyForUrl = function(str) {
   return str
     .toLowerCase()
-    .replace(/[^\w ]+/g, '')
-    .replace(/ +/g, '-');
+    .replace(/[^\w ]+/g, "")
+    .replace(/ +/g, "-");
 };
 
 kbn.stringToJsRegex = function(str) {
-  if (str[0] !== '/') {
-    return new RegExp('^' + str + '$');
+  if (str[0] !== "/") {
+    return new RegExp("^" + str + "$");
   }
 
-  var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?)$'));
+  var match = str.match(new RegExp("^/(.*?)/(g?i?m?y?)$"));
   return new RegExp(match[1], match[2]);
 };
 
 kbn.toFixed = function(value, decimals) {
   if (value === null) {
-    return '';
+    return "";
   }
 
   var factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1;
   var formatted = String(Math.round(value * factor) / factor);
 
   // if exponent return directly
-  if (formatted.indexOf('e') !== -1 || value === 0) {
+  if (formatted.indexOf("e") !== -1 || value === 0) {
     return formatted;
   }
 
   // If tickDecimals was specified, ensure that we have exactly that
   // much precision; otherwise default to the value's own precision.
   if (decimals != null) {
-    var decimalPos = formatted.indexOf('.');
+    var decimalPos = formatted.indexOf(".");
     var precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1;
     if (precision < decimals) {
-      return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision);
+      return (
+        (precision ? formatted : formatted + ".") +
+        String(factor).substr(1, decimals - precision)
+      );
     }
   }
 
   return formatted;
 };
 
-kbn.toFixedScaled = function(value, decimals, scaledDecimals, additionalDecimals, ext) {
+kbn.toFixedScaled = function(
+  value,
+  decimals,
+  scaledDecimals,
+  additionalDecimals,
+  ext
+) {
   if (scaledDecimals === null) {
     return kbn.toFixed(value, decimals) + ext;
   } else {
@@ -278,9 +295,9 @@ kbn.formatBuilders = {};
 kbn.formatBuilders.fixedUnit = function(unit) {
   return function(size, decimals) {
     if (size === null) {
-      return '';
+      return "";
     }
-    return kbn.toFixed(size, decimals) + ' ' + unit;
+    return kbn.toFixed(size, decimals) + " " + unit;
   };
 };
 
@@ -290,7 +307,7 @@ kbn.formatBuilders.fixedUnit = function(unit) {
 kbn.formatBuilders.scaledUnits = function(factor, extArray) {
   return function(size, decimals, scaledDecimals) {
     if (size === null) {
-      return '';
+      return "";
     }
 
     var steps = 0;
@@ -301,7 +318,7 @@ kbn.formatBuilders.scaledUnits = function(factor, extArray) {
       size /= factor;
 
       if (steps >= limit) {
-        return 'NA';
+        return "NA";
       }
     }
 
@@ -317,10 +334,10 @@ kbn.formatBuilders.scaledUnits = function(factor, extArray) {
 // offset is given, it adjusts the starting units at the given prefix; a value
 // of 0 starts at no scale; -3 drops to nano, +2 starts at mega, etc.
 kbn.formatBuilders.decimalSIPrefix = function(unit, offset) {
-  var prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
+  var prefixes = ["n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
   prefixes = prefixes.slice(3 + (offset || 0));
   var units = prefixes.map(function(p) {
-    return ' ' + p + unit;
+    return " " + p + unit;
   });
   return kbn.formatBuilders.scaledUnits(1000, units);
 };
@@ -329,9 +346,11 @@ kbn.formatBuilders.decimalSIPrefix = function(unit, offset) {
 // offset is given, it starts the units at the given prefix; otherwise, the
 // offset defaults to zero and the initial unit is not prefixed.
 kbn.formatBuilders.binarySIPrefix = function(unit, offset) {
-  var prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset);
+  var prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"].slice(
+    offset
+  );
   var units = prefixes.map(function(p) {
-    return ' ' + p + unit;
+    return " " + p + unit;
   });
   return kbn.formatBuilders.scaledUnits(1024, units);
 };
@@ -339,11 +358,11 @@ kbn.formatBuilders.binarySIPrefix = function(unit, offset) {
 // Currency formatter for prefixing a symbol onto a number. Supports scaling
 // up to the trillions.
 kbn.formatBuilders.currency = function(symbol) {
-  var units = ['', 'K', 'M', 'B', 'T'];
+  var units = ["", "K", "M", "B", "T"];
   var scaler = kbn.formatBuilders.scaledUnits(1000, units);
   return function(size, decimals, scaledDecimals) {
     if (size === null) {
-      return '';
+      return "";
     }
     var scaled = scaler(size, decimals, scaledDecimals);
     return symbol + scaled;
@@ -351,14 +370,14 @@ kbn.formatBuilders.currency = function(symbol) {
 };
 
 kbn.formatBuilders.simpleCountUnit = function(symbol) {
-  var units = ['', 'K', 'M', 'B', 'T'];
+  var units = ["", "K", "M", "B", "T"];
   var scaler = kbn.formatBuilders.scaledUnits(1000, units);
   return function(size, decimals, scaledDecimals) {
     if (size === null) {
-      return '';
+      return "";
     }
     var scaled = scaler(size, decimals, scaledDecimals);
-    return scaled + ' ' + symbol;
+    return scaled + " " + symbol;
   };
 };
 
@@ -367,31 +386,31 @@ kbn.formatBuilders.simpleCountUnit = function(symbol) {
 // Dimensionless Units
 kbn.valueFormats.none = kbn.toFixed;
 kbn.valueFormats.short = kbn.formatBuilders.scaledUnits(1000, [
-  '',
-  ' K',
-  ' Mil',
-  ' Bil',
-  ' Tri',
-  ' Quadr',
-  ' Quint',
-  ' Sext',
-  ' Sept',
+  "",
+  " K",
+  " Mil",
+  " Bil",
+  " Tri",
+  " Quadr",
+  " Quint",
+  " Sext",
+  " Sept"
 ]);
-kbn.valueFormats.dB = kbn.formatBuilders.fixedUnit('dB');
-kbn.valueFormats.ppm = kbn.formatBuilders.fixedUnit('ppm');
+kbn.valueFormats.dB = kbn.formatBuilders.fixedUnit("dB");
+kbn.valueFormats.ppm = kbn.formatBuilders.fixedUnit("ppm");
 
 kbn.valueFormats.percent = function(size, decimals) {
   if (size === null) {
-    return '';
+    return "";
   }
-  return kbn.toFixed(size, decimals) + '%';
+  return kbn.toFixed(size, decimals) + "%";
 };
 
 kbn.valueFormats.percentunit = function(size, decimals) {
   if (size === null) {
-    return '';
+    return "";
   }
-  return kbn.toFixed(100 * size, decimals) + '%';
+  return kbn.toFixed(100 * size, decimals) + "%";
 };
 
 /* Formats the value to hex. Uses float if specified decimals are not 0.
@@ -399,7 +418,7 @@ kbn.valueFormats.percentunit = function(size, decimals) {
 
 kbn.valueFormats.hex = function(value, decimals) {
   if (value == null) {
-    return '';
+    return "";
   }
   return parseFloat(kbn.toFixed(value, decimals))
     .toString(16)
@@ -408,13 +427,13 @@ kbn.valueFormats.hex = function(value, decimals) {
 
 kbn.valueFormats.hex0x = function(value, decimals) {
   if (value == null) {
-    return '';
+    return "";
   }
   var hexString = kbn.valueFormats.hex(value, decimals);
-  if (hexString.substring(0, 1) === '-') {
-    return '-0x' + hexString.substring(1);
+  if (hexString.substring(0, 1) === "-") {
+    return "-0x" + hexString.substring(1);
   }
-  return '0x' + hexString;
+  return "0x" + hexString;
 };
 
 kbn.valueFormats.sci = function(value, decimals) {
@@ -426,305 +445,387 @@ kbn.valueFormats.locale = function(value, decimals) {
 };
 
 // Currencies
-kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency('$');
-kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency('£');
-kbn.valueFormats.currencyEUR = kbn.formatBuilders.currency('€');
-kbn.valueFormats.currencyJPY = kbn.formatBuilders.currency('¥');
-kbn.valueFormats.currencyRUB = kbn.formatBuilders.currency('₽');
-kbn.valueFormats.currencyUAH = kbn.formatBuilders.currency('₴');
-kbn.valueFormats.currencyBRL = kbn.formatBuilders.currency('R$');
-kbn.valueFormats.currencyDKK = kbn.formatBuilders.currency('kr');
-kbn.valueFormats.currencyISK = kbn.formatBuilders.currency('kr');
-kbn.valueFormats.currencyNOK = kbn.formatBuilders.currency('kr');
-kbn.valueFormats.currencySEK = kbn.formatBuilders.currency('kr');
+kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency("$");
+kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency("£");
+kbn.valueFormats.currencyEUR = kbn.formatBuilders.currency("€");
+kbn.valueFormats.currencyJPY = kbn.formatBuilders.currency("¥");
+kbn.valueFormats.currencyRUB = kbn.formatBuilders.currency("₽");
+kbn.valueFormats.currencyUAH = kbn.formatBuilders.currency("₴");
+kbn.valueFormats.currencyBRL = kbn.formatBuilders.currency("R$");
+kbn.valueFormats.currencyDKK = kbn.formatBuilders.currency("kr");
+kbn.valueFormats.currencyISK = kbn.formatBuilders.currency("kr");
+kbn.valueFormats.currencyNOK = kbn.formatBuilders.currency("kr");
+kbn.valueFormats.currencySEK = kbn.formatBuilders.currency("kr");
 
 // Data (Binary)
-kbn.valueFormats.bits = kbn.formatBuilders.binarySIPrefix('b');
-kbn.valueFormats.bytes = kbn.formatBuilders.binarySIPrefix('B');
-kbn.valueFormats.kbytes = kbn.formatBuilders.binarySIPrefix('B', 1);
-kbn.valueFormats.mbytes = kbn.formatBuilders.binarySIPrefix('B', 2);
-kbn.valueFormats.gbytes = kbn.formatBuilders.binarySIPrefix('B', 3);
+kbn.valueFormats.bits = kbn.formatBuilders.binarySIPrefix("b");
+kbn.valueFormats.bytes = kbn.formatBuilders.binarySIPrefix("B");
+kbn.valueFormats.kbytes = kbn.formatBuilders.binarySIPrefix("B", 1);
+kbn.valueFormats.mbytes = kbn.formatBuilders.binarySIPrefix("B", 2);
+kbn.valueFormats.gbytes = kbn.formatBuilders.binarySIPrefix("B", 3);
 
 // Data (Decimal)
-kbn.valueFormats.decbits = kbn.formatBuilders.decimalSIPrefix('b');
-kbn.valueFormats.decbytes = kbn.formatBuilders.decimalSIPrefix('B');
-kbn.valueFormats.deckbytes = kbn.formatBuilders.decimalSIPrefix('B', 1);
-kbn.valueFormats.decmbytes = kbn.formatBuilders.decimalSIPrefix('B', 2);
-kbn.valueFormats.decgbytes = kbn.formatBuilders.decimalSIPrefix('B', 3);
+kbn.valueFormats.decbits = kbn.formatBuilders.decimalSIPrefix("b");
+kbn.valueFormats.decbytes = kbn.formatBuilders.decimalSIPrefix("B");
+kbn.valueFormats.deckbytes = kbn.formatBuilders.decimalSIPrefix("B", 1);
+kbn.valueFormats.decmbytes = kbn.formatBuilders.decimalSIPrefix("B", 2);
+kbn.valueFormats.decgbytes = kbn.formatBuilders.decimalSIPrefix("B", 3);
 
 // Data Rate
-kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps');
-kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps');
-kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('Bps');
-kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix('Bs', 1);
-kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix('bps', 1);
-kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix('Bs', 2);
-kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix('bps', 2);
-kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix('Bs', 3);
-kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix('bps', 3);
+kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix("pps");
+kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix("bps");
+kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix("Bps");
+kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix("Bs", 1);
+kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix("bps", 1);
+kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix("Bs", 2);
+kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix("bps", 2);
+kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix("Bs", 3);
+kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix("bps", 3);
 
 // Throughput
-kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops');
-kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit('rps');
-kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit('wps');
-kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit('iops');
-kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit('opm');
-kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit('rpm');
-kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit('wpm');
+kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit("ops");
+kbn.valueFormats.rps = kbn.formatBuilders.simpleCountUnit("rps");
+kbn.valueFormats.wps = kbn.formatBuilders.simpleCountUnit("wps");
+kbn.valueFormats.iops = kbn.formatBuilders.simpleCountUnit("iops");
+kbn.valueFormats.opm = kbn.formatBuilders.simpleCountUnit("opm");
+kbn.valueFormats.rpm = kbn.formatBuilders.simpleCountUnit("rpm");
+kbn.valueFormats.wpm = kbn.formatBuilders.simpleCountUnit("wpm");
 
 // Energy
-kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix('W');
-kbn.valueFormats.kwatt = kbn.formatBuilders.decimalSIPrefix('W', 1);
-kbn.valueFormats.mwatt = kbn.formatBuilders.decimalSIPrefix('W', -1);
-kbn.valueFormats.kwattm = kbn.formatBuilders.decimalSIPrefix('W/Min', 1);
-kbn.valueFormats.voltamp = kbn.formatBuilders.decimalSIPrefix('VA');
-kbn.valueFormats.kvoltamp = kbn.formatBuilders.decimalSIPrefix('VA', 1);
-kbn.valueFormats.voltampreact = kbn.formatBuilders.decimalSIPrefix('var');
-kbn.valueFormats.kvoltampreact = kbn.formatBuilders.decimalSIPrefix('var', 1);
-kbn.valueFormats.watth = kbn.formatBuilders.decimalSIPrefix('Wh');
-kbn.valueFormats.kwatth = kbn.formatBuilders.decimalSIPrefix('Wh', 1);
-kbn.valueFormats.joule = kbn.formatBuilders.decimalSIPrefix('J');
-kbn.valueFormats.ev = kbn.formatBuilders.decimalSIPrefix('eV');
-kbn.valueFormats.amp = kbn.formatBuilders.decimalSIPrefix('A');
-kbn.valueFormats.kamp = kbn.formatBuilders.decimalSIPrefix('A', 1);
-kbn.valueFormats.mamp = kbn.formatBuilders.decimalSIPrefix('A', -1);
-kbn.valueFormats.volt = kbn.formatBuilders.decimalSIPrefix('V');
-kbn.valueFormats.kvolt = kbn.formatBuilders.decimalSIPrefix('V', 1);
-kbn.valueFormats.mvolt = kbn.formatBuilders.decimalSIPrefix('V', -1);
-kbn.valueFormats.dBm = kbn.formatBuilders.decimalSIPrefix('dBm');
-kbn.valueFormats.ohm = kbn.formatBuilders.decimalSIPrefix('Ω');
+kbn.valueFormats.watt = kbn.formatBuilders.decimalSIPrefix("W");
+kbn.valueFormats.kwatt = kbn.formatBuilders.decimalSIPrefix("W", 1);
+kbn.valueFormats.mwatt = kbn.formatBuilders.decimalSIPrefix("W", -1);
+kbn.valueFormats.kwattm = kbn.formatBuilders.decimalSIPrefix("W/Min", 1);
+kbn.valueFormats.voltamp = kbn.formatBuilders.decimalSIPrefix("VA");
+kbn.valueFormats.kvoltamp = kbn.formatBuilders.decimalSIPrefix("VA", 1);
+kbn.valueFormats.voltampreact = kbn.formatBuilders.decimalSIPrefix("var");
+kbn.valueFormats.kvoltampreact = kbn.formatBuilders.decimalSIPrefix("var", 1);
+kbn.valueFormats.watth = kbn.formatBuilders.decimalSIPrefix("Wh");
+kbn.valueFormats.kwatth = kbn.formatBuilders.decimalSIPrefix("Wh", 1);
+kbn.valueFormats.joule = kbn.formatBuilders.decimalSIPrefix("J");
+kbn.valueFormats.ev = kbn.formatBuilders.decimalSIPrefix("eV");
+kbn.valueFormats.amp = kbn.formatBuilders.decimalSIPrefix("A");
+kbn.valueFormats.kamp = kbn.formatBuilders.decimalSIPrefix("A", 1);
+kbn.valueFormats.mamp = kbn.formatBuilders.decimalSIPrefix("A", -1);
+kbn.valueFormats.volt = kbn.formatBuilders.decimalSIPrefix("V");
+kbn.valueFormats.kvolt = kbn.formatBuilders.decimalSIPrefix("V", 1);
+kbn.valueFormats.mvolt = kbn.formatBuilders.decimalSIPrefix("V", -1);
+kbn.valueFormats.dBm = kbn.formatBuilders.decimalSIPrefix("dBm");
+kbn.valueFormats.ohm = kbn.formatBuilders.decimalSIPrefix("Ω");
 
 // Temperature
-kbn.valueFormats.celsius = kbn.formatBuilders.fixedUnit('°C');
-kbn.valueFormats.farenheit = kbn.formatBuilders.fixedUnit('°F');
-kbn.valueFormats.kelvin = kbn.formatBuilders.fixedUnit('K');
-kbn.valueFormats.humidity = kbn.formatBuilders.fixedUnit('%H');
+kbn.valueFormats.celsius = kbn.formatBuilders.fixedUnit("°C");
+kbn.valueFormats.farenheit = kbn.formatBuilders.fixedUnit("°F");
+kbn.valueFormats.kelvin = kbn.formatBuilders.fixedUnit("K");
+kbn.valueFormats.humidity = kbn.formatBuilders.fixedUnit("%H");
 
 // Pressure
-kbn.valueFormats.pressurebar = kbn.formatBuilders.decimalSIPrefix('bar');
-kbn.valueFormats.pressurembar = kbn.formatBuilders.decimalSIPrefix('bar', -1);
-kbn.valueFormats.pressurekbar = kbn.formatBuilders.decimalSIPrefix('bar', 1);
-kbn.valueFormats.pressurehpa = kbn.formatBuilders.fixedUnit('hPa');
+kbn.valueFormats.pressurebar = kbn.formatBuilders.decimalSIPrefix("bar");
+kbn.valueFormats.pressurembar = kbn.formatBuilders.decimalSIPrefix("bar", -1);
+kbn.valueFormats.pressurekbar = kbn.formatBuilders.decimalSIPrefix("bar", 1);
+kbn.valueFormats.pressurehpa = kbn.formatBuilders.fixedUnit("hPa");
 kbn.valueFormats.pressurehg = kbn.formatBuilders.fixedUnit('"Hg');
-kbn.valueFormats.pressurepsi = kbn.formatBuilders.scaledUnits(1000, [' psi', ' ksi', ' Mpsi']);
+kbn.valueFormats.pressurepsi = kbn.formatBuilders.scaledUnits(1000, [
+  " psi",
+  " ksi",
+  " Mpsi"
+]);
 
 // Force
-kbn.valueFormats.forceNm = kbn.formatBuilders.decimalSIPrefix('Nm');
-kbn.valueFormats.forcekNm = kbn.formatBuilders.decimalSIPrefix('Nm', 1);
-kbn.valueFormats.forceN = kbn.formatBuilders.decimalSIPrefix('N');
-kbn.valueFormats.forcekN = kbn.formatBuilders.decimalSIPrefix('N', 1);
+kbn.valueFormats.forceNm = kbn.formatBuilders.decimalSIPrefix("Nm");
+kbn.valueFormats.forcekNm = kbn.formatBuilders.decimalSIPrefix("Nm", 1);
+kbn.valueFormats.forceN = kbn.formatBuilders.decimalSIPrefix("N");
+kbn.valueFormats.forcekN = kbn.formatBuilders.decimalSIPrefix("N", 1);
 
 // Length
-kbn.valueFormats.lengthm = kbn.formatBuilders.decimalSIPrefix('m');
-kbn.valueFormats.lengthmm = kbn.formatBuilders.decimalSIPrefix('m', -1);
-kbn.valueFormats.lengthkm = kbn.formatBuilders.decimalSIPrefix('m', 1);
-kbn.valueFormats.lengthmi = kbn.formatBuilders.fixedUnit('mi');
-kbn.valueFormats.lengthft = kbn.formatBuilders.fixedUnit('ft');
+kbn.valueFormats.lengthm = kbn.formatBuilders.decimalSIPrefix("m");
+kbn.valueFormats.lengthmm = kbn.formatBuilders.decimalSIPrefix("m", -1);
+kbn.valueFormats.lengthkm = kbn.formatBuilders.decimalSIPrefix("m", 1);
+kbn.valueFormats.lengthmi = kbn.formatBuilders.fixedUnit("mi");
+kbn.valueFormats.lengthft = kbn.formatBuilders.fixedUnit("ft");
 
 // Area
-kbn.valueFormats.areaM2 = kbn.formatBuilders.fixedUnit('m²');
-kbn.valueFormats.areaF2 = kbn.formatBuilders.fixedUnit('ft²');
-kbn.valueFormats.areaMI2 = kbn.formatBuilders.fixedUnit('mi²');
+kbn.valueFormats.areaM2 = kbn.formatBuilders.fixedUnit("m²");
+kbn.valueFormats.areaF2 = kbn.formatBuilders.fixedUnit("ft²");
+kbn.valueFormats.areaMI2 = kbn.formatBuilders.fixedUnit("mi²");
 
 // Mass
-kbn.valueFormats.massmg = kbn.formatBuilders.decimalSIPrefix('g', -1);
-kbn.valueFormats.massg = kbn.formatBuilders.decimalSIPrefix('g');
-kbn.valueFormats.masskg = kbn.formatBuilders.decimalSIPrefix('g', 1);
-kbn.valueFormats.masst = kbn.formatBuilders.fixedUnit('t');
+kbn.valueFormats.massmg = kbn.formatBuilders.decimalSIPrefix("g", -1);
+kbn.valueFormats.massg = kbn.formatBuilders.decimalSIPrefix("g");
+kbn.valueFormats.masskg = kbn.formatBuilders.decimalSIPrefix("g", 1);
+kbn.valueFormats.masst = kbn.formatBuilders.fixedUnit("t");
 
 // Velocity
-kbn.valueFormats.velocityms = kbn.formatBuilders.fixedUnit('m/s');
-kbn.valueFormats.velocitykmh = kbn.formatBuilders.fixedUnit('km/h');
-kbn.valueFormats.velocitymph = kbn.formatBuilders.fixedUnit('mph');
-kbn.valueFormats.velocityknot = kbn.formatBuilders.fixedUnit('kn');
+kbn.valueFormats.velocityms = kbn.formatBuilders.fixedUnit("m/s");
+kbn.valueFormats.velocitykmh = kbn.formatBuilders.fixedUnit("km/h");
+kbn.valueFormats.velocitymph = kbn.formatBuilders.fixedUnit("mph");
+kbn.valueFormats.velocityknot = kbn.formatBuilders.fixedUnit("kn");
 
 // Acceleration
-kbn.valueFormats.accMS2 = kbn.formatBuilders.fixedUnit('m/sec²');
-kbn.valueFormats.accFS2 = kbn.formatBuilders.fixedUnit('f/sec²');
-kbn.valueFormats.accG   = kbn.formatBuilders.fixedUnit('g');
+kbn.valueFormats.accMS2 = kbn.formatBuilders.fixedUnit("m/sec²");
+kbn.valueFormats.accFS2 = kbn.formatBuilders.fixedUnit("f/sec²");
+kbn.valueFormats.accG = kbn.formatBuilders.fixedUnit("g");
 
 // Volume
-kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix('L');
-kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix('L', -1);
-kbn.valueFormats.m3 = kbn.formatBuilders.decimalSIPrefix('m3');
-kbn.valueFormats.dm3 = kbn.formatBuilders.decimalSIPrefix('dm3');
-kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit('gal');
+kbn.valueFormats.litre = kbn.formatBuilders.decimalSIPrefix("L");
+kbn.valueFormats.mlitre = kbn.formatBuilders.decimalSIPrefix("L", -1);
+kbn.valueFormats.m3 = kbn.formatBuilders.decimalSIPrefix("m3");
+kbn.valueFormats.dm3 = kbn.formatBuilders.decimalSIPrefix("dm3");
+kbn.valueFormats.gallons = kbn.formatBuilders.fixedUnit("gal");
 
 // Flow
-kbn.valueFormats.flowgpm = kbn.formatBuilders.fixedUnit('gpm');
-kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit('cms');
-kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit('cfs');
-kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit('cfm');
+kbn.valueFormats.flowgpm = kbn.formatBuilders.fixedUnit("gpm");
+kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit("cms");
+kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit("cfs");
+kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit("cfm");
 
 // Angle
-kbn.valueFormats.degree  = kbn.formatBuilders.fixedUnit('°');
-kbn.valueFormats.radian  = kbn.formatBuilders.fixedUnit('rad');
-kbn.valueFormats.grad    = kbn.formatBuilders.fixedUnit('grad');
+kbn.valueFormats.degree = kbn.formatBuilders.fixedUnit("°");
+kbn.valueFormats.radian = kbn.formatBuilders.fixedUnit("rad");
+kbn.valueFormats.grad = kbn.formatBuilders.fixedUnit("grad");
 
 // Time
-kbn.valueFormats.hertz = kbn.formatBuilders.decimalSIPrefix('Hz');
+kbn.valueFormats.hertz = kbn.formatBuilders.decimalSIPrefix("Hz");
 
 kbn.valueFormats.ms = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 1000) {
-    return kbn.toFixed(size, decimals) + ' ms';
+    return kbn.toFixed(size, decimals) + " ms";
   } else if (Math.abs(size) < 60000) {
     // Less than 1 min
-    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s');
+    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " s");
   } else if (Math.abs(size) < 3600000) {
     // Less than 1 hour, devide in minutes
-    return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min');
+    return kbn.toFixedScaled(size / 60000, decimals, scaledDecimals, 5, " min");
   } else if (Math.abs(size) < 86400000) {
     // Less than one day, devide in hours
-    return kbn.toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour');
+    return kbn.toFixedScaled(
+      size / 3600000,
+      decimals,
+      scaledDecimals,
+      7,
+      " hour"
+    );
   } else if (Math.abs(size) < 31536000000) {
     // Less than one year, devide in days
-    return kbn.toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day');
+    return kbn.toFixedScaled(
+      size / 86400000,
+      decimals,
+      scaledDecimals,
+      8,
+      " day"
+    );
   }
 
-  return kbn.toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year');
+  return kbn.toFixedScaled(
+    size / 31536000000,
+    decimals,
+    scaledDecimals,
+    10,
+    " year"
+  );
 };
 
 kbn.valueFormats.s = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   // Less than 1 µs, devide in ns
   if (Math.abs(size) < 0.000001) {
-    return kbn.toFixedScaled(size * 1e9, decimals, scaledDecimals - decimals, -9, ' ns');
+    return kbn.toFixedScaled(
+      size * 1e9,
+      decimals,
+      scaledDecimals - decimals,
+      -9,
+      " ns"
+    );
   }
   // Less than 1 ms, devide in µs
   if (Math.abs(size) < 0.001) {
-    return kbn.toFixedScaled(size * 1e6, decimals, scaledDecimals - decimals, -6, ' µs');
+    return kbn.toFixedScaled(
+      size * 1e6,
+      decimals,
+      scaledDecimals - decimals,
+      -6,
+      " µs"
+    );
   }
   // Less than 1 second, devide in ms
   if (Math.abs(size) < 1) {
-    return kbn.toFixedScaled(size * 1e3, decimals, scaledDecimals - decimals, -3, ' ms');
+    return kbn.toFixedScaled(
+      size * 1e3,
+      decimals,
+      scaledDecimals - decimals,
+      -3,
+      " ms"
+    );
   }
 
   if (Math.abs(size) < 60) {
-    return kbn.toFixed(size, decimals) + ' s';
+    return kbn.toFixed(size, decimals) + " s";
   } else if (Math.abs(size) < 3600) {
     // Less than 1 hour, devide in minutes
-    return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min');
+    return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 1, " min");
   } else if (Math.abs(size) < 86400) {
     // Less than one day, devide in hours
-    return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour');
+    return kbn.toFixedScaled(size / 3600, decimals, scaledDecimals, 4, " hour");
   } else if (Math.abs(size) < 604800) {
     // Less than one week, devide in days
-    return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day');
+    return kbn.toFixedScaled(size / 86400, decimals, scaledDecimals, 5, " day");
   } else if (Math.abs(size) < 31536000) {
     // Less than one year, devide in week
-    return kbn.toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week');
+    return kbn.toFixedScaled(
+      size / 604800,
+      decimals,
+      scaledDecimals,
+      6,
+      " week"
+    );
   }
 
-  return kbn.toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year');
+  return kbn.toFixedScaled(
+    size / 3.15569e7,
+    decimals,
+    scaledDecimals,
+    7,
+    " year"
+  );
 };
 
-kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) {
+kbn.valueFormats["µs"] = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 1000) {
-    return kbn.toFixed(size, decimals) + ' µs';
+    return kbn.toFixed(size, decimals) + " µs";
   } else if (Math.abs(size) < 1000000) {
-    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms');
+    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " ms");
   } else {
-    return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s');
+    return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, " s");
   }
 };
 
 kbn.valueFormats.ns = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 1000) {
-    return kbn.toFixed(size, decimals) + ' ns';
+    return kbn.toFixed(size, decimals) + " ns";
   } else if (Math.abs(size) < 1000000) {
-    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs');
+    return kbn.toFixedScaled(size / 1000, decimals, scaledDecimals, 3, " µs");
   } else if (Math.abs(size) < 1000000000) {
-    return kbn.toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms');
+    return kbn.toFixedScaled(
+      size / 1000000,
+      decimals,
+      scaledDecimals,
+      6,
+      " ms"
+    );
   } else if (Math.abs(size) < 60000000000) {
-    return kbn.toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s');
+    return kbn.toFixedScaled(
+      size / 1000000000,
+      decimals,
+      scaledDecimals,
+      9,
+      " s"
+    );
   } else {
-    return kbn.toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min');
+    return kbn.toFixedScaled(
+      size / 60000000000,
+      decimals,
+      scaledDecimals,
+      12,
+      " min"
+    );
   }
 };
 
 kbn.valueFormats.m = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 60) {
-    return kbn.toFixed(size, decimals) + ' min';
+    return kbn.toFixed(size, decimals) + " min";
   } else if (Math.abs(size) < 1440) {
-    return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour');
+    return kbn.toFixedScaled(size / 60, decimals, scaledDecimals, 2, " hour");
   } else if (Math.abs(size) < 10080) {
-    return kbn.toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day');
+    return kbn.toFixedScaled(size / 1440, decimals, scaledDecimals, 3, " day");
   } else if (Math.abs(size) < 604800) {
-    return kbn.toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week');
+    return kbn.toFixedScaled(
+      size / 10080,
+      decimals,
+      scaledDecimals,
+      4,
+      " week"
+    );
   } else {
-    return kbn.toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year');
+    return kbn.toFixedScaled(
+      size / 5.25948e5,
+      decimals,
+      scaledDecimals,
+      5,
+      " year"
+    );
   }
 };
 
 kbn.valueFormats.h = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 24) {
-    return kbn.toFixed(size, decimals) + ' hour';
+    return kbn.toFixed(size, decimals) + " hour";
   } else if (Math.abs(size) < 168) {
-    return kbn.toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day');
+    return kbn.toFixedScaled(size / 24, decimals, scaledDecimals, 2, " day");
   } else if (Math.abs(size) < 8760) {
-    return kbn.toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week');
+    return kbn.toFixedScaled(size / 168, decimals, scaledDecimals, 3, " week");
   } else {
-    return kbn.toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year');
+    return kbn.toFixedScaled(size / 8760, decimals, scaledDecimals, 4, " year");
   }
 };
 
 kbn.valueFormats.d = function(size, decimals, scaledDecimals) {
   if (size === null) {
-    return '';
+    return "";
   }
 
   if (Math.abs(size) < 7) {
-    return kbn.toFixed(size, decimals) + ' day';
+    return kbn.toFixed(size, decimals) + " day";
   } else if (Math.abs(size) < 365) {
-    return kbn.toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week');
+    return kbn.toFixedScaled(size / 7, decimals, scaledDecimals, 2, " week");
   } else {
-    return kbn.toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year');
+    return kbn.toFixedScaled(size / 365, decimals, scaledDecimals, 3, " year");
   }
 };
 
 kbn.toDuration = function(size, decimals, timeScale) {
   if (size === null) {
-    return '';
+    return "";
   }
   if (size === 0) {
-    return '0 ' + timeScale + 's';
+    return "0 " + timeScale + "s";
   }
   if (size < 0) {
-    return kbn.toDuration(-size, decimals, timeScale) + ' ago';
+    return kbn.toDuration(-size, decimals, timeScale) + " ago";
   }
 
   var units = [
-    { short: 'y', long: 'year' },
-    { short: 'M', long: 'month' },
-    { short: 'w', long: 'week' },
-    { short: 'd', long: 'day' },
-    { short: 'h', long: 'hour' },
-    { short: 'm', long: 'minute' },
-    { short: 's', long: 'second' },
-    { short: 'ms', long: 'millisecond' },
+    { short: "y", long: "year" },
+    { short: "M", long: "month" },
+    { short: "w", long: "week" },
+    { short: "d", long: "day" },
+    { short: "h", long: "hour" },
+    { short: "m", long: "minute" },
+    { short: "s", long: "second" },
+    { short: "ms", long: "millisecond" }
   ];
   // convert $size to milliseconds
   // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors
@@ -744,40 +845,40 @@ kbn.toDuration = function(size, decimals, timeScale) {
     if (value >= 1 || decrementDecimals) {
       decrementDecimals = true;
       var floor = Math.floor(value);
-      var unit = units[i].long + (floor !== 1 ? 's' : '');
-      strings.push(floor + ' ' + unit);
+      var unit = units[i].long + (floor !== 1 ? "s" : "");
+      strings.push(floor + " " + unit);
       size = size % interval;
       decimals--;
     }
   }
 
-  return strings.join(', ');
+  return strings.join(", ");
 };
 
 kbn.valueFormats.dtdurationms = function(size, decimals) {
-  return kbn.toDuration(size, decimals, 'millisecond');
+  return kbn.toDuration(size, decimals, "millisecond");
 };
 
 kbn.valueFormats.dtdurations = function(size, decimals) {
-  return kbn.toDuration(size, decimals, 'second');
+  return kbn.toDuration(size, decimals, "second");
 };
 
 kbn.valueFormats.dateTimeAsIso = function(epoch) {
   var time = moment(epoch);
 
-  if (moment().isSame(epoch, 'day')) {
-    return time.format('HH:mm:ss');
+  if (moment().isSame(epoch, "day")) {
+    return time.format("HH:mm:ss");
   }
-  return time.format('YYYY-MM-DD HH:mm:ss');
+  return time.format("YYYY-MM-DD HH:mm:ss");
 };
 
 kbn.valueFormats.dateTimeAsUS = function(epoch) {
   var time = moment(epoch);
 
-  if (moment().isSame(epoch, 'day')) {
-    return time.format('h:mm:ss a');
+  if (moment().isSame(epoch, "day")) {
+    return time.format("h:mm:ss a");
   }
-  return time.format('MM/DD/YYYY h:mm:ss a');
+  return time.format("MM/DD/YYYY h:mm:ss a");
 };
 
 kbn.valueFormats.dateTimeFromNow = function(epoch) {
@@ -789,228 +890,228 @@ kbn.valueFormats.dateTimeFromNow = function(epoch) {
 kbn.getUnitFormats = function() {
   return [
     {
-      text: 'none',
+      text: "none",
       submenu: [
-        { text: 'none', value: 'none' },
-        { text: 'short', value: 'short' },
-        { text: 'percent (0-100)', value: 'percent' },
-        { text: 'percent (0.0-1.0)', value: 'percentunit' },
-        { text: 'Humidity (%H)', value: 'humidity' },
-        { text: 'ppm', value: 'ppm' },
-        { text: 'decibel', value: 'dB' },
-        { text: 'hexadecimal (0x)', value: 'hex0x' },
-        { text: 'hexadecimal', value: 'hex' },
-        { text: 'scientific notation', value: 'sci' },
-        { text: 'locale format', value: 'locale' },
-      ],
+        { text: "none", value: "none" },
+        { text: "short", value: "short" },
+        { text: "percent (0-100)", value: "percent" },
+        { text: "percent (0.0-1.0)", value: "percentunit" },
+        { text: "Humidity (%H)", value: "humidity" },
+        { text: "ppm", value: "ppm" },
+        { text: "decibel", value: "dB" },
+        { text: "hexadecimal (0x)", value: "hex0x" },
+        { text: "hexadecimal", value: "hex" },
+        { text: "scientific notation", value: "sci" },
+        { text: "locale format", value: "locale" }
+      ]
     },
     {
-      text: 'currency',
+      text: "currency",
       submenu: [
-        { text: 'Dollars ($)', value: 'currencyUSD' },
-        { text: 'Pounds (£)', value: 'currencyGBP' },
-        { text: 'Euro (€)', value: 'currencyEUR' },
-        { text: 'Yen (¥)', value: 'currencyJPY' },
-        { text: 'Rubles (₽)', value: 'currencyRUB' },
-        { text: 'Hryvnias (₴)', value: 'currencyUAH' },
-        { text: 'Real (R$)', value: 'currencyBRL' },
-        { text: 'Danish Krone (kr)', value: 'currencyDKK' },
-        { text: 'Icelandic Krone (kr)', value: 'currencyISK' },
-        { text: 'Norwegian Krone (kr)', value: 'currencyNOK' },
-        { text: 'Swedish Krone (kr)', value: 'currencySEK' },
-      ],
+        { text: "Dollars ($)", value: "currencyUSD" },
+        { text: "Pounds (£)", value: "currencyGBP" },
+        { text: "Euro (€)", value: "currencyEUR" },
+        { text: "Yen (¥)", value: "currencyJPY" },
+        { text: "Rubles (₽)", value: "currencyRUB" },
+        { text: "Hryvnias (₴)", value: "currencyUAH" },
+        { text: "Real (R$)", value: "currencyBRL" },
+        { text: "Danish Krone (kr)", value: "currencyDKK" },
+        { text: "Icelandic Krone (kr)", value: "currencyISK" },
+        { text: "Norwegian Krone (kr)", value: "currencyNOK" },
+        { text: "Swedish Krone (kr)", value: "currencySEK" }
+      ]
     },
     {
-      text: 'time',
+      text: "time",
       submenu: [
-        { text: 'Hertz (1/s)', value: 'hertz' },
-        { text: 'nanoseconds (ns)', value: 'ns' },
-        { text: 'microseconds (µs)', value: 'µs' },
-        { text: 'milliseconds (ms)', value: 'ms' },
-        { text: 'seconds (s)', value: 's' },
-        { text: 'minutes (m)', value: 'm' },
-        { text: 'hours (h)', value: 'h' },
-        { text: 'days (d)', value: 'd' },
-        { text: 'duration (ms)', value: 'dtdurationms' },
-        { text: 'duration (s)', value: 'dtdurations' },
-      ],
+        { text: "Hertz (1/s)", value: "hertz" },
+        { text: "nanoseconds (ns)", value: "ns" },
+        { text: "microseconds (µs)", value: "µs" },
+        { text: "milliseconds (ms)", value: "ms" },
+        { text: "seconds (s)", value: "s" },
+        { text: "minutes (m)", value: "m" },
+        { text: "hours (h)", value: "h" },
+        { text: "days (d)", value: "d" },
+        { text: "duration (ms)", value: "dtdurationms" },
+        { text: "duration (s)", value: "dtdurations" }
+      ]
     },
     {
-      text: 'date & time',
+      text: "date & time",
       submenu: [
-        { text: 'YYYY-MM-DD HH:mm:ss', value: 'dateTimeAsIso' },
-        { text: 'DD/MM/YYYY h:mm:ss a', value: 'dateTimeAsUS' },
-        { text: 'From Now', value: 'dateTimeFromNow' },
-      ],
+        { text: "YYYY-MM-DD HH:mm:ss", value: "dateTimeAsIso" },
+        { text: "DD/MM/YYYY h:mm:ss a", value: "dateTimeAsUS" },
+        { text: "From Now", value: "dateTimeFromNow" }
+      ]
     },
     {
-      text: 'data (IEC)',
+      text: "data (IEC)",
       submenu: [
-        { text: 'bits', value: 'bits' },
-        { text: 'bytes', value: 'bytes' },
-        { text: 'kibibytes', value: 'kbytes' },
-        { text: 'mebibytes', value: 'mbytes' },
-        { text: 'gibibytes', value: 'gbytes' },
-      ],
+        { text: "bits", value: "bits" },
+        { text: "bytes", value: "bytes" },
+        { text: "kibibytes", value: "kbytes" },
+        { text: "mebibytes", value: "mbytes" },
+        { text: "gibibytes", value: "gbytes" }
+      ]
     },
     {
-      text: 'data (Metric)',
+      text: "data (Metric)",
       submenu: [
-        { text: 'bits', value: 'decbits' },
-        { text: 'bytes', value: 'decbytes' },
-        { text: 'kilobytes', value: 'deckbytes' },
-        { text: 'megabytes', value: 'decmbytes' },
-        { text: 'gigabytes', value: 'decgbytes' },
-      ],
+        { text: "bits", value: "decbits" },
+        { text: "bytes", value: "decbytes" },
+        { text: "kilobytes", value: "deckbytes" },
+        { text: "megabytes", value: "decmbytes" },
+        { text: "gigabytes", value: "decgbytes" }
+      ]
     },
     {
-      text: 'data rate',
+      text: "data rate",
       submenu: [
-        { text: 'packets/sec', value: 'pps' },
-        { text: 'bits/sec', value: 'bps' },
-        { text: 'bytes/sec', value: 'Bps' },
-        { text: 'kilobits/sec', value: 'Kbits' },
-        { text: 'kilobytes/sec', value: 'KBs' },
-        { text: 'megabits/sec', value: 'Mbits' },
-        { text: 'megabytes/sec', value: 'MBs' },
-        { text: 'gigabytes/sec', value: 'GBs' },
-        { text: 'gigabits/sec', value: 'Gbits' },
-      ],
+        { text: "packets/sec", value: "pps" },
+        { text: "bits/sec", value: "bps" },
+        { text: "bytes/sec", value: "Bps" },
+        { text: "kilobits/sec", value: "Kbits" },
+        { text: "kilobytes/sec", value: "KBs" },
+        { text: "megabits/sec", value: "Mbits" },
+        { text: "megabytes/sec", value: "MBs" },
+        { text: "gigabytes/sec", value: "GBs" },
+        { text: "gigabits/sec", value: "Gbits" }
+      ]
     },
     {
-      text: 'throughput',
+      text: "throughput",
       submenu: [
-        { text: 'ops/sec (ops)', value: 'ops' },
-        { text: 'reads/sec (rps)', value: 'rps' },
-        { text: 'writes/sec (wps)', value: 'wps' },
-        { text: 'I/O ops/sec (iops)', value: 'iops' },
-        { text: 'ops/min (opm)', value: 'opm' },
-        { text: 'reads/min (rpm)', value: 'rpm' },
-        { text: 'writes/min (wpm)', value: 'wpm' },
-      ],
+        { text: "ops/sec (ops)", value: "ops" },
+        { text: "reads/sec (rps)", value: "rps" },
+        { text: "writes/sec (wps)", value: "wps" },
+        { text: "I/O ops/sec (iops)", value: "iops" },
+        { text: "ops/min (opm)", value: "opm" },
+        { text: "reads/min (rpm)", value: "rpm" },
+        { text: "writes/min (wpm)", value: "wpm" }
+      ]
     },
     {
-      text: 'length',
+      text: "length",
       submenu: [
-        { text: 'millimetre (mm)', value: 'lengthmm' },
-        { text: 'meter (m)', value: 'lengthm' },
-        { text: 'feet (ft)', value: 'lengthft' },
-        { text: 'kilometer (km)', value: 'lengthkm' },
-        { text: 'mile (mi)', value: 'lengthmi' },
-      ],
+        { text: "millimetre (mm)", value: "lengthmm" },
+        { text: "meter (m)", value: "lengthm" },
+        { text: "feet (ft)", value: "lengthft" },
+        { text: "kilometer (km)", value: "lengthkm" },
+        { text: "mile (mi)", value: "lengthmi" }
+      ]
     },
     {
-      text: 'area',
+      text: "area",
       submenu: [
-        {text: 'Square Meters (m²)', value: 'areaM2' },
-        {text: 'Square Feet (ft²)',  value: 'areaF2' },
-        {text: 'Square Miles (mi²)', value: 'areaMI2'},
+        { text: "Square Meters (m²)", value: "areaM2" },
+        { text: "Square Feet (ft²)", value: "areaF2" },
+        { text: "Square Miles (mi²)", value: "areaMI2" }
       ]
     },
     {
-      text: 'mass',
+      text: "mass",
       submenu: [
-        { text: 'milligram (mg)', value: 'massmg' },
-        { text: 'gram (g)', value: 'massg' },
-        { text: 'kilogram (kg)', value: 'masskg' },
-        { text: 'metric ton (t)', value: 'masst' },
-      ],
+        { text: "milligram (mg)", value: "massmg" },
+        { text: "gram (g)", value: "massg" },
+        { text: "kilogram (kg)", value: "masskg" },
+        { text: "metric ton (t)", value: "masst" }
+      ]
     },
     {
-      text: 'velocity',
+      text: "velocity",
       submenu: [
-        { text: 'm/s', value: 'velocityms' },
-        { text: 'km/h', value: 'velocitykmh' },
-        { text: 'mph', value: 'velocitymph' },
-        { text: 'knot (kn)', value: 'velocityknot' },
-      ],
+        { text: "m/s", value: "velocityms" },
+        { text: "km/h", value: "velocitykmh" },
+        { text: "mph", value: "velocitymph" },
+        { text: "knot (kn)", value: "velocityknot" }
+      ]
     },
     {
-      text: 'volume',
+      text: "volume",
       submenu: [
-        { text: 'millilitre', value: 'mlitre' },
-        { text: 'litre', value: 'litre' },
-        { text: 'cubic metre', value: 'm3' },
-        { text: 'cubic decimetre', value: 'dm3' },
-        { text: 'gallons', value: 'gallons' },
-      ],
+        { text: "millilitre", value: "mlitre" },
+        { text: "litre", value: "litre" },
+        { text: "cubic metre", value: "m3" },
+        { text: "cubic decimetre", value: "dm3" },
+        { text: "gallons", value: "gallons" }
+      ]
     },
     {
-      text: 'energy',
+      text: "energy",
       submenu: [
-        { text: 'Watt (W)', value: 'watt' },
-        { text: 'Kilowatt (kW)', value: 'kwatt' },
-        { text: 'Milliwatt (mW)', value: 'mwatt' },
-        { text: 'Volt-ampere (VA)', value: 'voltamp' },
-        { text: 'Kilovolt-ampere (kVA)', value: 'kvoltamp' },
-        { text: 'Volt-ampere reactive (var)', value: 'voltampreact' },
-        { text: 'Kilovolt-ampere reactive (kvar)', value: 'kvoltampreact' },
-        { text: 'Watt-hour (Wh)', value: 'watth' },
-        { text: 'Kilowatt-hour (kWh)', value: 'kwatth' },
-        { text: 'Kilowatt-min (kWm)', value: 'kwattm' },
-        { text: 'Joule (J)', value: 'joule' },
-        { text: 'Electron volt (eV)', value: 'ev' },
-        { text: 'Ampere (A)', value: 'amp' },
-        { text: 'Kiloampere (kA)', value: 'kamp' },
-        { text: 'Milliampere (mA)', value: 'mamp' },
-        { text: 'Volt (V)', value: 'volt' },
-        { text: 'Kilovolt (kV)', value: 'kvolt' },
-        { text: 'Millivolt (mV)', value: 'mvolt' },
-        { text: 'Decibel-milliwatt (dBm)', value: 'dBm' },
-        { text: 'Ohm (Ω)', value: 'ohm' }
-      ],
+        { text: "Watt (W)", value: "watt" },
+        { text: "Kilowatt (kW)", value: "kwatt" },
+        { text: "Milliwatt (mW)", value: "mwatt" },
+        { text: "Volt-ampere (VA)", value: "voltamp" },
+        { text: "Kilovolt-ampere (kVA)", value: "kvoltamp" },
+        { text: "Volt-ampere reactive (var)", value: "voltampreact" },
+        { text: "Kilovolt-ampere reactive (kvar)", value: "kvoltampreact" },
+        { text: "Watt-hour (Wh)", value: "watth" },
+        { text: "Kilowatt-hour (kWh)", value: "kwatth" },
+        { text: "Kilowatt-min (kWm)", value: "kwattm" },
+        { text: "Joule (J)", value: "joule" },
+        { text: "Electron volt (eV)", value: "ev" },
+        { text: "Ampere (A)", value: "amp" },
+        { text: "Kiloampere (kA)", value: "kamp" },
+        { text: "Milliampere (mA)", value: "mamp" },
+        { text: "Volt (V)", value: "volt" },
+        { text: "Kilovolt (kV)", value: "kvolt" },
+        { text: "Millivolt (mV)", value: "mvolt" },
+        { text: "Decibel-milliwatt (dBm)", value: "dBm" },
+        { text: "Ohm (Ω)", value: "ohm" }
+      ]
     },
     {
-      text: 'temperature',
+      text: "temperature",
       submenu: [
-        { text: 'Celsius (°C)', value: 'celsius' },
-        { text: 'Farenheit (°F)', value: 'farenheit' },
-        { text: 'Kelvin (K)', value: 'kelvin' },
-      ],
+        { text: "Celsius (°C)", value: "celsius" },
+        { text: "Farenheit (°F)", value: "farenheit" },
+        { text: "Kelvin (K)", value: "kelvin" }
+      ]
     },
     {
-      text: 'pressure',
+      text: "pressure",
       submenu: [
-        { text: 'Millibars', value: 'pressurembar' },
-        { text: 'Bars', value: 'pressurebar' },
-        { text: 'Kilobars', value: 'pressurekbar' },
-        { text: 'Hectopascals', value: 'pressurehpa' },
-        { text: 'Inches of mercury', value: 'pressurehg' },
-        { text: 'PSI', value: 'pressurepsi' },
-      ],
+        { text: "Millibars", value: "pressurembar" },
+        { text: "Bars", value: "pressurebar" },
+        { text: "Kilobars", value: "pressurekbar" },
+        { text: "Hectopascals", value: "pressurehpa" },
+        { text: "Inches of mercury", value: "pressurehg" },
+        { text: "PSI", value: "pressurepsi" }
+      ]
     },
     {
-      text: 'force',
+      text: "force",
       submenu: [
-        { text: 'Newton-meters (Nm)', value: 'forceNm' },
-        { text: 'Kilonewton-meters (kNm)', value: 'forcekNm' },
-        { text: 'Newtons (N)', value: 'forceN' },
-        { text: 'Kilonewtons (kN)', value: 'forcekN' },
-      ],
+        { text: "Newton-meters (Nm)", value: "forceNm" },
+        { text: "Kilonewton-meters (kNm)", value: "forcekNm" },
+        { text: "Newtons (N)", value: "forceN" },
+        { text: "Kilonewtons (kN)", value: "forcekN" }
+      ]
     },
     {
-      text: 'flow',
+      text: "flow",
       submenu: [
-        { text: 'Gallons/min (gpm)', value: 'flowgpm' },
-        { text: 'Cubic meters/sec (cms)', value: 'flowcms' },
-        { text: 'Cubic feet/sec (cfs)', value: 'flowcfs' },
-        { text: 'Cubic feet/min (cfm)', value: 'flowcfm' },
-      ],
+        { text: "Gallons/min (gpm)", value: "flowgpm" },
+        { text: "Cubic meters/sec (cms)", value: "flowcms" },
+        { text: "Cubic feet/sec (cfs)", value: "flowcfs" },
+        { text: "Cubic feet/min (cfm)", value: "flowcfm" }
+      ]
     },
     {
-      text: 'angle',
+      text: "angle",
       submenu: [
-        { text: 'Degrees (°)', value: 'degree' },
-        { text: 'Radians', value: 'radian' },
-        { text: 'Gradian', value: 'grad' }
+        { text: "Degrees (°)", value: "degree" },
+        { text: "Radians", value: "radian" },
+        { text: "Gradian", value: "grad" }
       ]
     },
     {
-      text: 'acceleration',
+      text: "acceleration",
       submenu: [
-        { text: 'Meters/sec²', value: 'accMS2' },
-        { text: 'Feet/sec²',  value: 'accFS2' },
-        { text: 'G unit',  value: 'accG' }
+        { text: "Meters/sec²", value: "accMS2" },
+        { text: "Feet/sec²", value: "accFS2" },
+        { text: "G unit", value: "accG" }
       ]
     }
   ];

+ 6 - 2
public/app/core/utils/model_utils.ts

@@ -1,4 +1,9 @@
-export function assignModelProperties(target, source, defaults, removeDefaults?) {
+export function assignModelProperties(
+  target,
+  source,
+  defaults,
+  removeDefaults?
+) {
   for (var key in defaults) {
     if (!defaults.hasOwnProperty(key)) {
       continue;
@@ -7,4 +12,3 @@ export function assignModelProperties(target, source, defaults, removeDefaults?)
     target[key] = source[key] === undefined ? defaults[key] : source[key];
   }
 }
-

+ 13 - 11
public/app/core/utils/outline.ts

@@ -2,32 +2,34 @@
 function outlineFixer() {
   let d: any = document;
 
-  var style_element = d.createElement('STYLE');
-  var dom_events = 'addEventListener' in d;
+  var style_element = d.createElement("STYLE");
+  var dom_events = "addEventListener" in d;
 
-  var add_event_listener = function (type, callback) {
+  var add_event_listener = function(type, callback) {
     // Basic cross-browser event handling
     if (dom_events) {
       d.addEventListener(type, callback);
     } else {
-      d.attachEvent('on' + type, callback);
+      d.attachEvent("on" + type, callback);
     }
   };
 
-  var set_css = function (css_text) {
+  var set_css = function(css_text) {
     // Handle setting of <style> element contents in IE8
-    !!style_element.styleSheet ? style_element.styleSheet.cssText = css_text : style_element.innerHTML = css_text;
+    !!style_element.styleSheet
+      ? (style_element.styleSheet.cssText = css_text)
+      : (style_element.innerHTML = css_text);
   };
 
-  d.getElementsByTagName('HEAD')[0].appendChild(style_element);
+  d.getElementsByTagName("HEAD")[0].appendChild(style_element);
 
   // Using mousedown instead of mouseover, so that previously focused elements don't lose focus ring on mouse move
-  add_event_listener('mousedown', function () {
-    set_css(':focus{outline:0 !important}::-moz-focus-inner{border:0;}');
+  add_event_listener("mousedown", function() {
+    set_css(":focus{outline:0 !important}::-moz-focus-inner{border:0;}");
   });
 
-  add_event_listener('keydown', function () {
-    set_css('');
+  add_event_listener("keydown", function() {
+    set_css("");
   });
 }
 

+ 73 - 63
public/app/core/utils/rangeutil.ts

@@ -1,63 +1,73 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
-import moment from 'moment';
-import * as dateMath from './datemath';
+import _ from "lodash";
+import moment from "moment";
+import * as dateMath from "./datemath";
 
 var spans = {
-  's': {display: 'second'},
-  'm': {display: 'minute'},
-  'h': {display: 'hour'},
-  'd': {display: 'day'},
-  'w': {display: 'week'},
-  'M': {display: 'month'},
-  'y': {display: 'year'},
+  s: { display: "second" },
+  m: { display: "minute" },
+  h: { display: "hour" },
+  d: { display: "day" },
+  w: { display: "week" },
+  M: { display: "month" },
+  y: { display: "year" }
 };
 
 var rangeOptions = [
-  { from: 'now/d',    to: 'now/d',    display: 'Today',                 section: 2 },
-  { from: 'now/d',    to: 'now',      display: 'Today so far',          section: 2 },
-  { from: 'now/w',    to: 'now/w',    display: 'This week',             section: 2 },
-  { from: 'now/w',    to: 'now',      display: 'This week so far',      section: 2 },
-  { from: 'now/M',    to: 'now/M',    display: 'This month',            section: 2 },
-  { from: 'now/M',    to: 'now',      display: 'This month so far',     section: 2 },
-  { from: 'now/y',    to: 'now/y',    display: 'This year',             section: 2 },
-  { from: 'now/y',    to: 'now',      display: 'This year so far',      section: 2 },
-
-  { from: 'now-1d/d', to: 'now-1d/d', display: 'Yesterday',             section: 1 },
-  { from: 'now-2d/d', to: 'now-2d/d', display: 'Day before yesterday',  section: 1 },
-  { from: 'now-7d/d', to: 'now-7d/d', display: 'This day last week',    section: 1 },
-  { from: 'now-1w/w', to: 'now-1w/w', display: 'Previous week',         section: 1 },
-  { from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month',        section: 1 },
-  { from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year',         section: 1 },
-
-  { from: 'now-5m',   to: 'now',      display: 'Last 5 minutes',        section: 3 },
-  { from: 'now-15m',  to: 'now',      display: 'Last 15 minutes',       section: 3 },
-  { from: 'now-30m',  to: 'now',      display: 'Last 30 minutes',       section: 3 },
-  { from: 'now-1h',   to: 'now',      display: 'Last 1 hour',           section: 3 },
-  { from: 'now-3h',   to: 'now',      display: 'Last 3 hours',          section: 3 },
-  { from: 'now-6h',   to: 'now',      display: 'Last 6 hours',          section: 3 },
-  { from: 'now-12h',  to: 'now',      display: 'Last 12 hours',         section: 3 },
-  { from: 'now-24h',  to: 'now',      display: 'Last 24 hours',         section: 3 },
-
-  { from: 'now-2d',   to: 'now',      display: 'Last 2 days',           section: 0 },
-  { from: 'now-7d',   to: 'now',      display: 'Last 7 days',           section: 0 },
-  { from: 'now-30d',  to: 'now',      display: 'Last 30 days',          section: 0 },
-  { from: 'now-90d',  to: 'now',      display: 'Last 90 days',          section: 0 },
-  { from: 'now-6M',   to: 'now',      display: 'Last 6 months',         section: 0 },
-  { from: 'now-1y',   to: 'now',      display: 'Last 1 year',           section: 0 },
-  { from: 'now-2y',   to: 'now',      display: 'Last 2 years',          section: 0 },
-  { from: 'now-5y',   to: 'now',      display: 'Last 5 years',          section: 0 },
+  { from: "now/d", to: "now/d", display: "Today", section: 2 },
+  { from: "now/d", to: "now", display: "Today so far", section: 2 },
+  { from: "now/w", to: "now/w", display: "This week", section: 2 },
+  { from: "now/w", to: "now", display: "This week so far", section: 2 },
+  { from: "now/M", to: "now/M", display: "This month", section: 2 },
+  { from: "now/M", to: "now", display: "This month so far", section: 2 },
+  { from: "now/y", to: "now/y", display: "This year", section: 2 },
+  { from: "now/y", to: "now", display: "This year so far", section: 2 },
+
+  { from: "now-1d/d", to: "now-1d/d", display: "Yesterday", section: 1 },
+  {
+    from: "now-2d/d",
+    to: "now-2d/d",
+    display: "Day before yesterday",
+    section: 1
+  },
+  {
+    from: "now-7d/d",
+    to: "now-7d/d",
+    display: "This day last week",
+    section: 1
+  },
+  { from: "now-1w/w", to: "now-1w/w", display: "Previous week", section: 1 },
+  { from: "now-1M/M", to: "now-1M/M", display: "Previous month", section: 1 },
+  { from: "now-1y/y", to: "now-1y/y", display: "Previous year", section: 1 },
+
+  { from: "now-5m", to: "now", display: "Last 5 minutes", section: 3 },
+  { from: "now-15m", to: "now", display: "Last 15 minutes", section: 3 },
+  { from: "now-30m", to: "now", display: "Last 30 minutes", section: 3 },
+  { from: "now-1h", to: "now", display: "Last 1 hour", section: 3 },
+  { from: "now-3h", to: "now", display: "Last 3 hours", section: 3 },
+  { from: "now-6h", to: "now", display: "Last 6 hours", section: 3 },
+  { from: "now-12h", to: "now", display: "Last 12 hours", section: 3 },
+  { from: "now-24h", to: "now", display: "Last 24 hours", section: 3 },
+
+  { from: "now-2d", to: "now", display: "Last 2 days", section: 0 },
+  { from: "now-7d", to: "now", display: "Last 7 days", section: 0 },
+  { from: "now-30d", to: "now", display: "Last 30 days", section: 0 },
+  { from: "now-90d", to: "now", display: "Last 90 days", section: 0 },
+  { from: "now-6M", to: "now", display: "Last 6 months", section: 0 },
+  { from: "now-1y", to: "now", display: "Last 1 year", section: 0 },
+  { from: "now-2y", to: "now", display: "Last 2 years", section: 0 },
+  { from: "now-5y", to: "now", display: "Last 5 years", section: 0 }
 ];
 
-var absoluteFormat = 'MMM D, YYYY HH:mm:ss';
+var absoluteFormat = "MMM D, YYYY HH:mm:ss";
 
 var rangeIndex = {};
-_.each(rangeOptions, function (frame) {
-  rangeIndex[frame.from + ' to ' + frame.to] = frame;
+_.each(rangeOptions, function(frame) {
+  rangeIndex[frame.from + " to " + frame.to] = frame;
 });
 
-export  function getRelativeTimesList(timepickerSettings, currentDisplay) {
+export function getRelativeTimesList(timepickerSettings, currentDisplay) {
   var groups = _.groupBy(rangeOptions, (option: any) => {
     option.active = option.display === currentDisplay;
     return option.section;
@@ -84,20 +94,20 @@ function formatDate(date) {
 // now/d
 // if no to <expr> then to now is assumed
 export function describeTextRange(expr: any) {
-  let isLast = (expr.indexOf('+') !== 0);
-  if (expr.indexOf('now') === -1) {
-    expr = (isLast ? 'now-' : 'now') + expr;
+  let isLast = expr.indexOf("+") !== 0;
+  if (expr.indexOf("now") === -1) {
+    expr = (isLast ? "now-" : "now") + expr;
   }
 
-  let opt = rangeIndex[expr + ' to now'];
+  let opt = rangeIndex[expr + " to now"];
   if (opt) {
     return opt;
   }
 
   if (isLast) {
-    opt = {from: expr, to: 'now'};
+    opt = { from: expr, to: "now" };
   } else {
-    opt = {from: 'now', to: expr};
+    opt = { from: "now", to: expr };
   }
 
   let parts = /^now([-+])(\d+)(\w)/.exec(expr);
@@ -106,15 +116,15 @@ export function describeTextRange(expr: any) {
     let amount = parseInt(parts[2]);
     let span = spans[unit];
     if (span) {
-      opt.display = isLast ? 'Last ' : 'Next ';
-      opt.display += amount + ' ' + span.display;
+      opt.display = isLast ? "Last " : "Next ";
+      opt.display += amount + " " + span.display;
       opt.section = span.section;
       if (amount > 1) {
-        opt.display += 's';
+        opt.display += "s";
       }
     }
   } else {
-    opt.display = opt.from + ' to ' + opt.to;
+    opt.display = opt.from + " to " + opt.to;
     opt.invalid = true;
   }
 
@@ -122,29 +132,29 @@ export function describeTextRange(expr: any) {
 }
 
 export function describeTimeRange(range) {
-  var option = rangeIndex[range.from.toString() + ' to ' + range.to.toString()];
+  var option = rangeIndex[range.from.toString() + " to " + range.to.toString()];
   if (option) {
     return option.display;
   }
 
   if (moment.isMoment(range.from) && moment.isMoment(range.to)) {
-    return formatDate(range.from) + ' to ' + formatDate(range.to);
+    return formatDate(range.from) + " to " + formatDate(range.to);
   }
 
   if (moment.isMoment(range.from)) {
     var toMoment = dateMath.parse(range.to, true);
-    return formatDate(range.from) + ' to ' + toMoment.fromNow();
+    return formatDate(range.from) + " to " + toMoment.fromNow();
   }
 
   if (moment.isMoment(range.to)) {
     var from = dateMath.parse(range.from, false);
-    return from.fromNow() + ' to ' + formatDate(range.to);
+    return from.fromNow() + " to " + formatDate(range.to);
   }
 
-  if (range.to.toString() === 'now') {
+  if (range.to.toString() === "now") {
     var res = describeTextRange(range.from);
     return res.display;
   }
 
-  return range.from.toString() + ' to ' + range.to.toString();
+  return range.from.toString() + " to " + range.to.toString();
 }

+ 12 - 5
public/app/core/utils/react2angular.ts

@@ -1,7 +1,14 @@
-import coreModule from 'app/core/core_module';
+import coreModule from "app/core/core_module";
 
-export function react2AngularDirective(name: string, component: any, options: any) {
-  coreModule.directive(name, ['reactDirective', reactDirective => {
-    return reactDirective(component, options);
-  }]);
+export function react2AngularDirective(
+  name: string,
+  component: any,
+  options: any
+) {
+  coreModule.directive(name, [
+    "reactDirective",
+    reactDirective => {
+      return reactDirective(component, options);
+    }
+  ]);
 }

+ 23 - 18
public/app/core/utils/ticks.ts

@@ -1,4 +1,4 @@
-import {getDataMinMax} from 'app/core/time_series2';
+import { getDataMinMax } from "app/core/time_series2";
 
 /**
  * Calculate tick step.
@@ -40,7 +40,12 @@ export function getScaledDecimals(decimals, tick_size) {
  * @param noTicks       Number of ticks
  * @param tickDecimals  Tick decimal precision
  */
-export function getFlotTickSize(min: number, max: number, noTicks: number, tickDecimals: number) {
+export function getFlotTickSize(
+  min: number,
+  max: number,
+  noTicks: number,
+  tickDecimals: number
+) {
   var delta = (max - min) / noTicks,
     dec = -Math.floor(Math.log(delta) / Math.LN10),
     maxDec = tickDecimals;
@@ -81,19 +86,19 @@ export function getFlotRange(panelMin, panelMax, datamin, datamax) {
   let delta = max - min;
 
   if (delta === 0.0) {
-      // Grafana fix: wide Y min and max using increased wideFactor
-      // when all series values are the same
-      var wideFactor = 0.25;
-      var widen = Math.abs(max === 0 ? 1 : max * wideFactor);
+    // Grafana fix: wide Y min and max using increased wideFactor
+    // when all series values are the same
+    var wideFactor = 0.25;
+    var widen = Math.abs(max === 0 ? 1 : max * wideFactor);
 
-      if (panelMin === null) {
-        min -= widen;
-      }
-      // always widen max if we couldn't widen min to ensure we
-      // don't fall into min == max which doesn't work
-      if (panelMax == null || panelMin != null) {
-        max += widen;
-      }
+    if (panelMin === null) {
+      min -= widen;
+    }
+    // always widen max if we couldn't widen min to ensure we
+    // don't fall into min == max which doesn't work
+    if (panelMax == null || panelMin != null) {
+      max += widen;
+    }
   } else {
     // consider autoscaling
     var margin = autoscaleMargin;
@@ -114,7 +119,7 @@ export function getFlotRange(panelMin, panelMax, datamin, datamax) {
       }
     }
   }
-  return {min, max};
+  return { min, max };
 }
 
 /**
@@ -122,8 +127,8 @@ export function getFlotRange(panelMin, panelMax, datamin, datamax) {
  * Implementation from Flot.
  */
 export function getFlotTickDecimals(data, axis) {
-  let {datamin, datamax} = getDataMinMax(data);
-  let {min, max} = getFlotRange(axis.min, axis.max, datamin, datamax);
+  let { datamin, datamax } = getDataMinMax(data);
+  let { min, max } = getFlotRange(axis.min, axis.max, datamin, datamax);
   let noTicks = 3;
   let tickDecimals, maxDec;
   let delta = (max - min) / noTicks;
@@ -154,5 +159,5 @@ export function getFlotTickDecimals(data, axis) {
   tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
   // grafana addition
   const scaledDecimals = tickDecimals - Math.floor(Math.log(size) / Math.LN10);
-  return {tickDecimals, scaledDecimals};
+  return { tickDecimals, scaledDecimals };
 }

+ 6 - 2
public/app/core/utils/version.ts

@@ -1,4 +1,4 @@
-import _ from 'lodash';
+import _ from "lodash";
 
 const versionPattern = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z\.]+))?/;
 
@@ -20,7 +20,11 @@ export class SemVersion {
 
   isGtOrEq(version: string): boolean {
     let compared = new SemVersion(version);
-    return !(this.major < compared.major || this.minor < compared.minor || this.patch < compared.patch);
+    return !(
+      this.major < compared.major ||
+      this.minor < compared.minor ||
+      this.patch < compared.patch
+    );
   }
 
   isValid(): boolean {

+ 14 - 15
public/app/features/admin/admin.ts

@@ -1,21 +1,20 @@
-import  AdminListUsersCtrl from './admin_list_users_ctrl';
-import  './admin_list_orgs_ctrl';
-import  './admin_edit_org_ctrl';
-import  './admin_edit_user_ctrl';
+import AdminListUsersCtrl from "./admin_list_users_ctrl";
+import "./admin_list_orgs_ctrl";
+import "./admin_edit_org_ctrl";
+import "./admin_edit_user_ctrl";
 
-import coreModule from 'app/core/core_module';
+import coreModule from "app/core/core_module";
 
 class AdminSettingsCtrl {
   navModel: any;
 
   /** @ngInject **/
   constructor($scope, backendSrv, navModelSrv) {
-    this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-settings', 1);
+    this.navModel = navModelSrv.getNav("cfg", "admin", "server-settings", 1);
 
-    backendSrv.get('/api/admin/settings').then(function(settings) {
+    backendSrv.get("/api/admin/settings").then(function(settings) {
       $scope.settings = settings;
     });
-
   }
 }
 
@@ -24,7 +23,7 @@ class AdminHomeCtrl {
 
   /** @ngInject **/
   constructor(navModelSrv) {
-    this.navModel = navModelSrv.getNav('cfg', 'admin', 1);
+    this.navModel = navModelSrv.getNav("cfg", "admin", 1);
   }
 }
 
@@ -34,15 +33,15 @@ export class AdminStatsCtrl {
 
   /** @ngInject */
   constructor(backendSrv: any, navModelSrv) {
-    this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-stats', 1);
+    this.navModel = navModelSrv.getNav("cfg", "admin", "server-stats", 1);
 
-    backendSrv.get('/api/admin/stats').then(stats => {
+    backendSrv.get("/api/admin/stats").then(stats => {
       this.stats = stats;
     });
   }
 }
 
-coreModule.controller('AdminSettingsCtrl', AdminSettingsCtrl);
-coreModule.controller('AdminHomeCtrl', AdminHomeCtrl);
-coreModule.controller('AdminStatsCtrl', AdminStatsCtrl);
-coreModule.controller('AdminListUsersCtrl', AdminListUsersCtrl);
+coreModule.controller("AdminSettingsCtrl", AdminSettingsCtrl);
+coreModule.controller("AdminHomeCtrl", AdminHomeCtrl);
+coreModule.controller("AdminStatsCtrl", AdminStatsCtrl);
+coreModule.controller("AdminListUsersCtrl", AdminListUsersCtrl);

+ 22 - 14
public/app/features/admin/admin_edit_org_ctrl.ts

@@ -1,11 +1,10 @@
-import angular from 'angular';
+import angular from "angular";
 
 export class AdminEditOrgCtrl {
-
   /** @ngInject */
   constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
     $scope.init = function() {
-      $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
+      $scope.navModel = navModelSrv.getNav("cfg", "admin", "global-orgs", 1);
 
       if ($routeParams.id) {
         $scope.getOrg($routeParams.id);
@@ -14,37 +13,46 @@ export class AdminEditOrgCtrl {
     };
 
     $scope.getOrg = function(id) {
-      backendSrv.get('/api/orgs/' + id).then(function(org) {
+      backendSrv.get("/api/orgs/" + id).then(function(org) {
         $scope.org = org;
       });
     };
 
     $scope.getOrgUsers = function(id) {
-      backendSrv.get('/api/orgs/' + id + '/users').then(function(orgUsers) {
+      backendSrv.get("/api/orgs/" + id + "/users").then(function(orgUsers) {
         $scope.orgUsers = orgUsers;
       });
     };
 
     $scope.update = function() {
-      if (!$scope.orgDetailsForm.$valid) { return; }
+      if (!$scope.orgDetailsForm.$valid) {
+        return;
+      }
 
-      backendSrv.put('/api/orgs/' + $scope.org.id, $scope.org).then(function() {
-        $location.path('/admin/orgs');
+      backendSrv.put("/api/orgs/" + $scope.org.id, $scope.org).then(function() {
+        $location.path("/admin/orgs");
       });
     };
 
-    $scope.updateOrgUser= function(orgUser) {
-      backendSrv.patch('/api/orgs/' + orgUser.orgId + '/users/' + orgUser.userId, orgUser);
+    $scope.updateOrgUser = function(orgUser) {
+      backendSrv.patch(
+        "/api/orgs/" + orgUser.orgId + "/users/" + orgUser.userId,
+        orgUser
+      );
     };
 
     $scope.removeOrgUser = function(orgUser) {
-      backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + orgUser.userId).then(function() {
-        $scope.getOrgUsers($scope.org.id);
-      });
+      backendSrv
+        .delete("/api/orgs/" + orgUser.orgId + "/users/" + orgUser.userId)
+        .then(function() {
+          $scope.getOrgUsers($scope.org.id);
+        });
     };
 
     $scope.init();
   }
 }
 
-angular.module('grafana.controllers').controller('AdminEditOrgCtrl', AdminEditOrgCtrl);
+angular
+  .module("grafana.controllers")
+  .controller("AdminEditOrgCtrl", AdminEditOrgCtrl);

+ 63 - 36
public/app/features/admin/admin_edit_user_ctrl.ts

@@ -1,14 +1,13 @@
-import angular from 'angular';
-import _ from 'lodash';
+import angular from "angular";
+import _ from "lodash";
 
 export class AdminEditUserCtrl {
-
   /** @ngInject */
   constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
     $scope.user = {};
-    $scope.newOrg = { name: '', role: 'Editor' };
+    $scope.newOrg = { name: "", role: "Editor" };
     $scope.permissions = {};
-    $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1);
+    $scope.navModel = navModelSrv.getNav("cfg", "admin", "global-users", 1);
 
     $scope.init = function() {
       if ($routeParams.id) {
@@ -18,61 +17,79 @@ export class AdminEditUserCtrl {
     };
 
     $scope.getUser = function(id) {
-      backendSrv.get('/api/users/' + id).then(function(user) {
+      backendSrv.get("/api/users/" + id).then(function(user) {
         $scope.user = user;
         $scope.user_id = id;
         $scope.permissions.isGrafanaAdmin = user.isGrafanaAdmin;
       });
     };
 
-    $scope.setPassword = function () {
-      if (!$scope.passwordForm.$valid) { return; }
+    $scope.setPassword = function() {
+      if (!$scope.passwordForm.$valid) {
+        return;
+      }
 
       var payload = { password: $scope.password };
-      backendSrv.put('/api/admin/users/' + $scope.user_id + '/password', payload).then(function() {
-        $location.path('/admin/users');
-      });
+      backendSrv
+        .put("/api/admin/users/" + $scope.user_id + "/password", payload)
+        .then(function() {
+          $location.path("/admin/users");
+        });
     };
 
     $scope.updatePermissions = function() {
       var payload = $scope.permissions;
 
-      backendSrv.put('/api/admin/users/' + $scope.user_id + '/permissions', payload).then(function() {
-        $location.path('/admin/users');
-      });
+      backendSrv
+        .put("/api/admin/users/" + $scope.user_id + "/permissions", payload)
+        .then(function() {
+          $location.path("/admin/users");
+        });
     };
 
     $scope.create = function() {
-      if (!$scope.userForm.$valid) { return; }
+      if (!$scope.userForm.$valid) {
+        return;
+      }
 
-      backendSrv.post('/api/admin/users', $scope.user).then(function() {
-        $location.path('/admin/users');
+      backendSrv.post("/api/admin/users", $scope.user).then(function() {
+        $location.path("/admin/users");
       });
     };
 
     $scope.getUserOrgs = function(id) {
-      backendSrv.get('/api/users/' + id + '/orgs').then(function(orgs) {
+      backendSrv.get("/api/users/" + id + "/orgs").then(function(orgs) {
         $scope.orgs = orgs;
       });
     };
 
     $scope.update = function() {
-      if (!$scope.userForm.$valid) { return; }
+      if (!$scope.userForm.$valid) {
+        return;
+      }
 
-      backendSrv.put('/api/users/' + $scope.user_id, $scope.user).then(function() {
-        $location.path('/admin/users');
-      });
+      backendSrv
+        .put("/api/users/" + $scope.user_id, $scope.user)
+        .then(function() {
+          $location.path("/admin/users");
+        });
     };
 
-    $scope.updateOrgUser= function(orgUser) {
-      backendSrv.patch('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id, orgUser).then(function() {
-      });
+    $scope.updateOrgUser = function(orgUser) {
+      backendSrv
+        .patch(
+          "/api/orgs/" + orgUser.orgId + "/users/" + $scope.user_id,
+          orgUser
+        )
+        .then(function() {});
     };
 
     $scope.removeOrgUser = function(orgUser) {
-      backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id).then(function() {
-        $scope.getUserOrgs($scope.user_id);
-      });
+      backendSrv
+        .delete("/api/orgs/" + orgUser.orgId + "/users/" + $scope.user_id)
+        .then(function() {
+          $scope.getUserOrgs($scope.user_id);
+        });
     };
 
     $scope.orgsSearchCache = [];
@@ -83,27 +100,37 @@ export class AdminEditUserCtrl {
         return;
       }
 
-      backendSrv.get('/api/orgs', {query: ''}).then(function(result) {
+      backendSrv.get("/api/orgs", { query: "" }).then(function(result) {
         $scope.orgsSearchCache = result;
         callback(_.map(result, "name"));
       });
     };
 
     $scope.addOrgUser = function() {
-      if (!$scope.addOrgForm.$valid) { return; }
+      if (!$scope.addOrgForm.$valid) {
+        return;
+      }
 
-      var orgInfo = _.find($scope.orgsSearchCache, {name: $scope.newOrg.name});
-      if (!orgInfo) { return; }
+      var orgInfo = _.find($scope.orgsSearchCache, {
+        name: $scope.newOrg.name
+      });
+      if (!orgInfo) {
+        return;
+      }
 
       $scope.newOrg.loginOrEmail = $scope.user.login;
 
-      backendSrv.post('/api/orgs/' + orgInfo.id + '/users/', $scope.newOrg).then(function() {
-        $scope.getUserOrgs($scope.user_id);
-      });
+      backendSrv
+        .post("/api/orgs/" + orgInfo.id + "/users/", $scope.newOrg)
+        .then(function() {
+          $scope.getUserOrgs($scope.user_id);
+        });
     };
 
     $scope.init();
   }
 }
 
-angular.module('grafana.controllers').controller('AdminEditUserCtrl', AdminEditUserCtrl);
+angular
+  .module("grafana.controllers")
+  .controller("AdminEditUserCtrl", AdminEditUserCtrl);

+ 13 - 12
public/app/features/admin/admin_list_orgs_ctrl.ts

@@ -1,29 +1,28 @@
-import angular from 'angular';
+import angular from "angular";
 
 export class AdminListOrgsCtrl {
-
   /** @ngInject */
   constructor($scope, backendSrv, navModelSrv) {
     $scope.init = function() {
-      $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
+      $scope.navModel = navModelSrv.getNav("cfg", "admin", "global-orgs", 1);
       $scope.getOrgs();
     };
 
     $scope.getOrgs = function() {
-      backendSrv.get('/api/orgs').then(function(orgs) {
+      backendSrv.get("/api/orgs").then(function(orgs) {
         $scope.orgs = orgs;
       });
     };
 
     $scope.deleteOrg = function(org) {
-      $scope.appEvent('confirm-modal', {
-        title: 'Delete',
-        text: 'Do you want to delete organization ' + org.name + '?',
-        text2: 'All dashboards for this organization will be removed!',
-        icon: 'fa-trash',
-        yesText: 'Delete',
+      $scope.appEvent("confirm-modal", {
+        title: "Delete",
+        text: "Do you want to delete organization " + org.name + "?",
+        text2: "All dashboards for this organization will be removed!",
+        icon: "fa-trash",
+        yesText: "Delete",
         onConfirm: function() {
-          backendSrv.delete('/api/orgs/' + org.id).then(function() {
+          backendSrv.delete("/api/orgs/" + org.id).then(function() {
             $scope.getOrgs();
           });
         }
@@ -34,4 +33,6 @@ export class AdminListOrgsCtrl {
   }
 }
 
-angular.module('grafana.controllers').controller('AdminListOrgsCtrl', AdminListOrgsCtrl);
+angular
+  .module("grafana.controllers")
+  .controller("AdminListOrgsCtrl", AdminListOrgsCtrl);

+ 25 - 19
public/app/features/admin/admin_list_users_ctrl.ts

@@ -10,24 +10,30 @@ export default class AdminListUsersCtrl {
 
   /** @ngInject */
   constructor(private $scope, private backendSrv, navModelSrv) {
-    this.navModel = navModelSrv.getNav('cfg', 'admin', 'global-users', 1);
-    this.query = '';
+    this.navModel = navModelSrv.getNav("cfg", "admin", "global-users", 1);
+    this.query = "";
     this.getUsers();
   }
 
   getUsers() {
-    this.backendSrv.get(`/api/users/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`).then((result) => {
-      this.users = result.users;
-      this.page = result.page;
-      this.perPage = result.perPage;
-      this.totalPages = Math.ceil(result.totalCount / result.perPage);
-      this.showPaging = this.totalPages > 1;
-      this.pages = [];
+    this.backendSrv
+      .get(
+        `/api/users/search?perpage=${this.perPage}&page=${this.page}&query=${
+          this.query
+        }`
+      )
+      .then(result => {
+        this.users = result.users;
+        this.page = result.page;
+        this.perPage = result.perPage;
+        this.totalPages = Math.ceil(result.totalCount / result.perPage);
+        this.showPaging = this.totalPages > 1;
+        this.pages = [];
 
-      for (var i = 1; i < this.totalPages+1; i++) {
-        this.pages.push({ page: i, current: i === this.page});
-      }
-    });
+        for (var i = 1; i < this.totalPages + 1; i++) {
+          this.pages.push({ page: i, current: i === this.page });
+        }
+      });
   }
 
   navigateToPage(page) {
@@ -36,13 +42,13 @@ export default class AdminListUsersCtrl {
   }
 
   deleteUser(user) {
-    this.$scope.appEvent('confirm-modal', {
-      title: 'Delete',
-      text: 'Do you want to delete ' + user.login + '?',
-      icon: 'fa-trash',
-      yesText: 'Delete',
+    this.$scope.appEvent("confirm-modal", {
+      title: "Delete",
+      text: "Do you want to delete " + user.login + "?",
+      icon: "fa-trash",
+      yesText: "Delete",
       onConfirm: () => {
-        this.backendSrv.delete('/api/admin/users/' + user.id).then(() => {
+        this.backendSrv.delete("/api/admin/users/" + user.id).then(() => {
           this.getUsers();
         });
       }

+ 75 - 69
public/app/features/alerting/alert_def.ts

@@ -1,131 +1,137 @@
 ///<reference path="../../headers/common.d.ts" />
 
-import _ from 'lodash';
+import _ from "lodash";
 import {
   QueryPartDef,
-  QueryPart,
-} from 'app/core/components/query_part/query_part';
+  QueryPart
+} from "app/core/components/query_part/query_part";
 
 var alertQueryDef = new QueryPartDef({
-  type: 'query',
+  type: "query",
   params: [
-    {name: "queryRefId", type: 'string', dynamicLookup: true},
-    {name: "from", type: "string", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h', '24h', '48h']},
-    {name: "to", type: "string", options: ['now']},
+    { name: "queryRefId", type: "string", dynamicLookup: true },
+    {
+      name: "from",
+      type: "string",
+      options: ["1s", "10s", "1m", "5m", "10m", "15m", "1h", "24h", "48h"]
+    },
+    { name: "to", type: "string", options: ["now"] }
   ],
-  defaultParams: ['#A', '15m', 'now', 'avg']
+  defaultParams: ["#A", "15m", "now", "avg"]
 });
 
-var conditionTypes = [
-  {text: 'Query', value: 'query'},
-];
+var conditionTypes = [{ text: "Query", value: "query" }];
 
 var alertStateSortScore = {
   alerting: 1,
   no_data: 2,
   pending: 3,
   ok: 4,
-  paused: 5,
+  paused: 5
 };
 
 var evalFunctions = [
-  {text: 'IS ABOVE', value: 'gt'},
-  {text: 'IS BELOW', value: 'lt'},
-  {text: 'IS OUTSIDE RANGE', value: 'outside_range'},
-  {text: 'IS WITHIN RANGE', value: 'within_range'},
-  {text: 'HAS NO VALUE' , value: 'no_value'}
+  { text: "IS ABOVE", value: "gt" },
+  { text: "IS BELOW", value: "lt" },
+  { text: "IS OUTSIDE RANGE", value: "outside_range" },
+  { text: "IS WITHIN RANGE", value: "within_range" },
+  { text: "HAS NO VALUE", value: "no_value" }
 ];
 
 var evalOperators = [
-  {text: 'OR', value: 'or'},
-  {text: 'AND', value: 'and'},
+  { text: "OR", value: "or" },
+  { text: "AND", value: "and" }
 ];
 
 var reducerTypes = [
-  {text: 'avg()', value: 'avg'},
-  {text: 'min()', value: 'min'},
-  {text: 'max()', value: 'max'},
-  {text: 'sum()' , value: 'sum'},
-  {text: 'count()', value: 'count'},
-  {text: 'last()', value: 'last'},
-  {text: 'median()', value: 'median'},
-  {text: 'diff()', value: 'diff'},
-  {text: 'percent_diff()', value: 'percent_diff'},
-  {text: 'count_non_null()', value: 'count_non_null'},
+  { text: "avg()", value: "avg" },
+  { text: "min()", value: "min" },
+  { text: "max()", value: "max" },
+  { text: "sum()", value: "sum" },
+  { text: "count()", value: "count" },
+  { text: "last()", value: "last" },
+  { text: "median()", value: "median" },
+  { text: "diff()", value: "diff" },
+  { text: "percent_diff()", value: "percent_diff" },
+  { text: "count_non_null()", value: "count_non_null" }
 ];
 
 var noDataModes = [
-  {text: 'Alerting', value: 'alerting'},
-  {text: 'No Data', value: 'no_data'},
-  {text: 'Keep Last State', value: 'keep_state'},
-  {text: 'Ok', value: 'ok'},
+  { text: "Alerting", value: "alerting" },
+  { text: "No Data", value: "no_data" },
+  { text: "Keep Last State", value: "keep_state" },
+  { text: "Ok", value: "ok" }
 ];
 
 var executionErrorModes = [
-  {text: 'Alerting', value: 'alerting'},
-  {text: 'Keep Last State', value: 'keep_state'},
+  { text: "Alerting", value: "alerting" },
+  { text: "Keep Last State", value: "keep_state" }
 ];
 
 function createReducerPart(model) {
-  var def = new QueryPartDef({type: model.type, defaultParams: []});
+  var def = new QueryPartDef({ type: model.type, defaultParams: [] });
   return new QueryPart(model, def);
 }
 
 function getStateDisplayModel(state) {
   switch (state) {
-    case 'ok': {
+    case "ok": {
       return {
-        text: 'OK',
-        iconClass: 'icon-gf icon-gf-online',
-        stateClass: 'alert-state-ok'
+        text: "OK",
+        iconClass: "icon-gf icon-gf-online",
+        stateClass: "alert-state-ok"
       };
     }
-    case 'alerting': {
+    case "alerting": {
       return {
-        text: 'ALERTING',
-        iconClass: 'icon-gf icon-gf-critical',
-        stateClass: 'alert-state-critical'
+        text: "ALERTING",
+        iconClass: "icon-gf icon-gf-critical",
+        stateClass: "alert-state-critical"
       };
     }
-    case 'no_data': {
+    case "no_data": {
       return {
-        text: 'NO DATA',
+        text: "NO DATA",
         iconClass: "fa fa-question",
-        stateClass: 'alert-state-warning'
+        stateClass: "alert-state-warning"
       };
     }
-    case 'paused': {
+    case "paused": {
       return {
-        text: 'PAUSED',
+        text: "PAUSED",
         iconClass: "fa fa-pause",
-        stateClass: 'alert-state-paused'
+        stateClass: "alert-state-paused"
       };
     }
-    case 'pending': {
+    case "pending": {
       return {
-        text: 'PENDING',
+        text: "PENDING",
         iconClass: "fa fa-exclamation",
-        stateClass: 'alert-state-warning'
+        stateClass: "alert-state-warning"
       };
     }
   }
 
-  throw {message: 'Unknown alert state'};
+  throw { message: "Unknown alert state" };
 }
 
 function joinEvalMatches(matches, separator: string) {
-  return _.reduce(matches, (res, ev)=> {
-    if (ev.metric !== undefined && ev.value !== undefined) {
-      res.push(ev.metric + '=' + ev.value);
-    }
-
-    // For backwards compatibility . Should be be able to remove this after ~2017-06-01
-    if (ev.Metric !== undefined && ev.Value !== undefined) {
-      res.push(ev.Metric + '=' + ev.Value);
-    }
-
-    return res;
-  }, []).join(separator);
+  return _.reduce(
+    matches,
+    (res, ev) => {
+      if (ev.metric !== undefined && ev.value !== undefined) {
+        res.push(ev.metric + "=" + ev.value);
+      }
+
+      // For backwards compatibility . Should be be able to remove this after ~2017-06-01
+      if (ev.Metric !== undefined && ev.Value !== undefined) {
+        res.push(ev.Metric + "=" + ev.Value);
+      }
+
+      return res;
+    },
+    []
+  ).join(separator);
 }
 
 function getAlertAnnotationInfo(ah) {
@@ -134,9 +140,9 @@ function getAlertAnnotationInfo(ah) {
   // new way stores it in evalMatches property on new data object
 
   if (_.isArray(ah.data)) {
-    return joinEvalMatches(ah.data, ', ');
+    return joinEvalMatches(ah.data, ", ");
   } else if (_.isArray(ah.data.evalMatches)) {
-    return joinEvalMatches(ah.data.evalMatches, ', ');
+    return joinEvalMatches(ah.data.evalMatches, ", ");
   }
 
   if (ah.data.error) {
@@ -161,5 +167,5 @@ export default {
   reducerTypes: reducerTypes,
   createReducerPart: createReducerPart,
   getAlertAnnotationInfo: getAlertAnnotationInfo,
-  alertStateSortScore: alertStateSortScore,
+  alertStateSortScore: alertStateSortScore
 };

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.