Browse Source

Merge branch 'master' into postgres-query-builder

Sven Klemm 7 năm trước cách đây
mục cha
commit
265d5daf57
100 tập tin đã thay đổi với 441 bổ sung1698 xóa
  1. 3 0
      CHANGELOG.md
  2. 1 7
      Gopkg.lock
  3. 1 1
      package.json
  4. 2 0
      pkg/cmd/grafana-cli/utils/grafana_path.go
  5. 7 0
      public/app/containers/Explore/Explore.tsx
  6. 1 1
      public/app/containers/Explore/utils/debounce.ts
  7. 1 1
      public/app/containers/Explore/utils/dom.ts
  8. 0 2
      public/app/core/angular_wrappers.ts
  9. 0 1240
      public/app/core/components/Login/LoginBackground.tsx
  10. 3 3
      public/app/core/components/form_dropdown/form_dropdown.ts
  11. 63 41
      public/app/core/components/grafana_app.ts
  12. 10 10
      public/app/core/components/json_explorer/helpers.ts
  13. 4 4
      public/app/core/components/layout_selector/layout_selector.ts
  14. 1 1
      public/app/core/components/manage_dashboards/manage_dashboards.ts
  15. 1 1
      public/app/core/components/query_part/query_part.ts
  16. 5 5
      public/app/core/components/query_part/query_part_editor.ts
  17. 0 38
      public/app/core/components/scroll/scroll.ts
  18. 21 21
      public/app/core/components/sql_part/sql_part_editor.ts
  19. 3 3
      public/app/core/directives/misc.ts
  20. 5 5
      public/app/core/directives/rebuild_on_change.ts
  21. 3 3
      public/app/core/directives/tags.ts
  22. 1 1
      public/app/core/filters/filters.ts
  23. 2 2
      public/app/core/jquery_extended.ts
  24. 1 1
      public/app/core/nav_model_srv.ts
  25. 1 1
      public/app/core/partials.ts
  26. 3 3
      public/app/core/profiler.ts
  27. 1 1
      public/app/core/services/backend_srv.ts
  28. 2 2
      public/app/core/services/impression_srv.ts
  29. 18 8
      public/app/core/services/keybindingSrv.ts
  30. 4 4
      public/app/core/services/ng_react.ts
  31. 2 2
      public/app/core/services/popover_srv.ts
  32. 2 2
      public/app/core/services/segment_srv.ts
  33. 3 3
      public/app/core/specs/datemath.test.ts
  34. 3 3
      public/app/core/specs/table_model.test.ts
  35. 3 3
      public/app/core/specs/time_series.test.ts
  36. 10 10
      public/app/core/time_series2.ts
  37. 2 2
      public/app/core/utils/css_loader.ts
  38. 8 8
      public/app/core/utils/datemath.ts
  39. 6 6
      public/app/core/utils/kbn.ts
  40. 1 1
      public/app/core/utils/tags.ts
  41. 1 1
      public/app/core/utils/url.ts
  42. 2 2
      public/app/features/admin/admin.ts
  43. 1 1
      public/app/features/admin/admin_list_users_ctrl.ts
  44. 1 1
      public/app/features/alerting/threshold_mapper.ts
  45. 6 6
      public/app/features/annotations/annotation_tooltip.ts
  46. 1 1
      public/app/features/annotations/annotations_srv.ts
  47. 1 1
      public/app/features/annotations/event_editor.ts
  48. 1 1
      public/app/features/annotations/event_manager.ts
  49. 1 1
      public/app/features/dashboard/create_folder_ctrl.ts
  50. 1 1
      public/app/features/dashboard/dashboard_ctrl.ts
  51. 1 1
      public/app/features/dashboard/dashboard_loader_srv.ts
  52. 8 11
      public/app/features/dashboard/dashboard_migration.ts
  53. 14 6
      public/app/features/dashboard/dashboard_model.ts
  54. 10 4
      public/app/features/dashboard/dashnav/dashnav.html
  55. 4 0
      public/app/features/dashboard/dashnav/dashnav.ts
  56. 2 2
      public/app/features/dashboard/export_data/export_data_modal.ts
  57. 1 1
      public/app/features/dashboard/repeat_option/repeat_option.ts
  58. 2 2
      public/app/features/dashboard/settings/settings.ts
  59. 2 2
      public/app/features/dashboard/shareModalCtrl.ts
  60. 1 1
      public/app/features/dashboard/share_snapshot_ctrl.ts
  61. 3 3
      public/app/features/dashboard/specs/repeat.test.ts
  62. 1 1
      public/app/features/dashboard/time_srv.ts
  63. 1 1
      public/app/features/dashboard/timepicker/input_date.ts
  64. 11 13
      public/app/features/dashboard/timepicker/timepicker.html
  65. 3 1
      public/app/features/dashboard/timepicker/timepicker.ts
  66. 1 1
      public/app/features/dashboard/unsaved_changes_srv.ts
  67. 2 2
      public/app/features/dashboard/upload.ts
  68. 1 1
      public/app/features/dashlinks/editor.ts
  69. 1 1
      public/app/features/dashlinks/module.ts
  70. 1 1
      public/app/features/org/change_password_ctrl.ts
  71. 1 1
      public/app/features/org/create_team_ctrl.ts
  72. 1 1
      public/app/features/org/new_org_ctrl.ts
  73. 1 1
      public/app/features/org/org_api_keys_ctrl.ts
  74. 1 1
      public/app/features/org/org_details_ctrl.ts
  75. 1 1
      public/app/features/org/prefs_control.ts
  76. 1 1
      public/app/features/org/profile_ctrl.ts
  77. 1 1
      public/app/features/org/select_org_ctrl.ts
  78. 1 1
      public/app/features/org/user_invite_ctrl.ts
  79. 2 2
      public/app/features/panel/metrics_panel_ctrl.ts
  80. 1 1
      public/app/features/panel/metrics_tab.ts
  81. 2 2
      public/app/features/panel/panel_ctrl.ts
  82. 1 1
      public/app/features/panel/panel_header.ts
  83. 1 1
      public/app/features/panel/query_editor_row.ts
  84. 2 2
      public/app/features/panel/query_troubleshooter.ts
  85. 62 74
      public/app/features/playlist/partials/playlist.html
  86. 30 28
      public/app/features/playlist/partials/playlists.html
  87. 2 13
      public/app/features/playlist/playlist_edit_ctrl.ts
  88. 1 3
      public/app/features/playlist/playlist_routes.ts
  89. 1 1
      public/app/features/playlist/playlist_search.ts
  90. 16 26
      public/app/features/playlist/playlist_srv.ts
  91. 4 1
      public/app/features/playlist/playlists_ctrl.ts
  92. 1 1
      public/app/features/playlist/specs/playlist_edit_ctrl.test.ts
  93. 2 2
      public/app/features/plugins/datasource_srv.ts
  94. 2 2
      public/app/features/plugins/ds_edit_ctrl.ts
  95. 2 2
      public/app/features/plugins/plugin_component.ts
  96. 1 1
      public/app/features/styleguide/styleguide.ts
  97. 1 1
      public/app/features/templating/adhoc_variable.ts
  98. 1 1
      public/app/features/templating/constant_variable.ts
  99. 1 1
      public/app/features/templating/custom_variable.ts
  100. 3 3
      public/app/features/templating/datasource_variable.ts

+ 3 - 0
CHANGELOG.md

@@ -1,5 +1,6 @@
 # 5.3.0 (unreleased)
 
+* **Dashboard**: TV & Kiosk mode changes, new cycle view mode button in dashboard toolbar [#13025](https://github.com/grafana/grafana/pull/13025)
 * **OAuth**: Gitlab OAuth with support for filter by groups [#5623](https://github.com/grafana/grafana/issues/5623), thx [@BenoitKnecht](https://github.com/BenoitKnecht)
 * **Dataproxy**: Pass configured/auth headers to a Datasource [#10971](https://github.com/grafana/grafana/issues/10971), thx [@mrsiano](https://github.com/mrsiano)
 * **Cleanup**: Make temp file time to live configurable [#11607](https://github.com/grafana/grafana/issues/11607), thx [@xapon](https://github.com/xapon)
@@ -62,6 +63,8 @@ om/grafana/grafana/issues/12668)
 ### Breaking changes
 
 * Postgres datasource no longer automatically adds time column alias when using the $__timeGroup alias. However, there's code in place which should make this change backward compatible and shouldn't create any issues.
+* Kiosk mode now also hides submenu (variables)
+* ?inactive url parameter no longer supported, replaced with kiosk=tv url parameter
 
 ### New experimental features
 

+ 1 - 7
Gopkg.lock

@@ -427,12 +427,6 @@
   revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
   version = "v1.0.0"
 
-[[projects]]
-  branch = "master"
-  name = "github.com/shurcooL/sanitized_anchor_name"
-  packages = ["."]
-  revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
-
 [[projects]]
   name = "github.com/smartystreets/assertions"
   packages = [
@@ -679,6 +673,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "cb8e7fd81f23ec987fc4d5dd9d31ae0f1164bc2f30cbea2fe86e0d97dd945beb"
+  inputs-digest = "81a37e747b875cf870c1b9486fa3147e704dea7db8ba86f7cb942d3ddc01d3e3"
   solver-name = "gps-cdcl"
   solver-version = 1

+ 1 - 1
package.json

@@ -101,7 +101,7 @@
     "build": "grunt build",
     "test": "grunt test",
     "test:coverage": "grunt test --coverage=true",
-    "lint": "tslint -c tslint.json --project tsconfig.json --type-check",
+    "lint": "tslint -c tslint.json --project tsconfig.json",
     "jest": "jest --notify --watch",
     "api-tests": "jest --notify --watch --config=tests/api/jest.js",
     "precommit": "lint-staged && grunt precommit"

+ 2 - 0
pkg/cmd/grafana-cli/utils/grafana_path.go

@@ -42,6 +42,8 @@ func returnOsDefault(currentOs string) string {
 		return "/usr/local/var/lib/grafana/plugins"
 	case "freebsd":
 		return "/var/db/grafana/plugins"
+	case "openbsd":
+		return "/var/grafana/plugins"
 	default: //"linux"
 		return "/var/lib/grafana/plugins"
 	}

+ 7 - 0
public/app/containers/Explore/Explore.tsx

@@ -173,6 +173,12 @@ export class Explore extends React.Component<any, ExploreState> {
       datasource.init();
     }
 
+    // Keep queries but reset edit state
+    const nextQueries = this.state.queries.map(q => ({
+      ...q,
+      edited: false,
+    }));
+
     this.setState(
       {
         datasource,
@@ -182,6 +188,7 @@ export class Explore extends React.Component<any, ExploreState> {
         supportsLogs,
         supportsTable,
         datasourceLoading: false,
+        queries: nextQueries,
       },
       () => datasourceError === null && this.onSubmit()
     );

+ 1 - 1
public/app/containers/Explore/utils/debounce.ts

@@ -1,7 +1,7 @@
 // Based on underscore.js debounce()
 export default function debounce(func, wait) {
   let timeout;
-  return function() {
+  return function(this: any) {
     const context = this;
     const args = arguments;
     const later = function() {

+ 1 - 1
public/app/containers/Explore/utils/dom.ts

@@ -1,6 +1,6 @@
 // Node.closest() polyfill
 if ('Element' in window && !Element.prototype.closest) {
-  Element.prototype.closest = function(s) {
+  Element.prototype.closest = function(this: any, s) {
     const matches = (this.document || this.ownerDocument).querySelectorAll(s);
     let el = this;
     let i;

+ 0 - 2
public/app/core/angular_wrappers.ts

@@ -2,7 +2,6 @@ import { react2AngularDirective } from 'app/core/utils/react2angular';
 import { PasswordStrength } from './components/PasswordStrength';
 import PageHeader from './components/PageHeader/PageHeader';
 import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
-import LoginBackground from './components/Login/LoginBackground';
 import { SearchResult } from './components/search/SearchResult';
 import { TagFilter } from './components/TagFilter/TagFilter';
 import DashboardPermissions from './components/Permissions/DashboardPermissions';
@@ -11,7 +10,6 @@ export function registerAngularDirectives() {
   react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
   react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
   react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
-  react2AngularDirective('loginBackground', LoginBackground, []);
   react2AngularDirective('searchResult', SearchResult, []);
   react2AngularDirective('tagFilter', TagFilter, [
     'tags',

+ 0 - 1240
public/app/core/components/Login/LoginBackground.tsx

@@ -1,1240 +0,0 @@
-import React, { Component } from 'react';
-
-const xCount = 50;
-const yCount = 50;
-
-function Cell({ x, y, flipIndex }) {
-  const index = (y * xCount) + x;
-  const bgColor1 = getColor(x, y);
-  return (
-    <div className={`login-bg__item ${flipIndex === index ? 'login-bg-flip' : ''}`} key={index} style={{background: bgColor1}} />
-  );
-}
-
-function getRandomInt(min, max) {
-  min = Math.ceil(min);
-  max = Math.floor(max);
-  return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
-}
-
-export default class LoginBackground extends Component<any, any> {
-  cancelInterval: any;
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      flipIndex: null,
-    };
-
-    this.flipElements = this.flipElements.bind(this);
-  }
-
-  flipElements() {
-    const elementIndexToFlip = getRandomInt(0, (xCount * yCount) - 1);
-    this.setState(prevState => {
-      return {
-        ...prevState,
-        flipIndex: elementIndexToFlip,
-      };
-    });
-  }
-
-  componentWillMount() {
-    this.cancelInterval = setInterval(this.flipElements, 3000);
-  }
-
-  componentWillUnmount() {
-    clearInterval(this.cancelInterval);
-  }
-
-  render() {
-    console.log('re-render!', this.state.flipIndex);
-
-    return (
-      <div className="login-bg">
-        {Array.from(Array(yCount)).map((el, y) => {
-          return (
-            <div className="login-bg__row">
-              {Array.from(Array(xCount)).map((el2, x) => {
-                return (
-                  <Cell y={y} x={x} flipIndex={this.state.flipIndex} />
-                );
-              })}
-            </div>
-          );
-        })}
-      </div>
-    );
-  }
-}
-
-function getColor(x, y) {
-  const colors = [
-    '#14161A',
-    '#111920',
-    '#121E27',
-    '#13212B',
-    '#122029',
-    '#101C24',
-    '#0F1B23',
-    '#0F1B22',
-    '#111C24',
-    '#101A22',
-    '#101A21',
-    '#111D25',
-    '#101E27',
-    '#101D26',
-    '#101B23',
-    '#11191E',
-    '#131519',
-    '#131518',
-    '#101B21',
-    '#121F29',
-    '#10232D',
-    '#11212B',
-    '#0E1C25',
-    '#0E1C24',
-    '#111F29',
-    '#11222B',
-    '#101E28',
-    '#102028',
-    '#111F2A',
-    '#11202A',
-    '#11191F',
-    '#121417',
-    '#12191D',
-    '#101D25',
-    '#11212C',
-    '#10242F',
-    '#0F212B',
-    '#0F1E27',
-    '#0F1D26',
-    '#0F1F29',
-    '#0F2029',
-    '#11232E',
-    '#10212B',
-    '#10222C',
-    '#0F202A',
-    '#112530',
-    '#10252F',
-    '#0F242E',
-    '#10222D',
-    '#10202A',
-    '#0F1C24',
-    '#0F1E28',
-    '#0F212A',
-    '#0F222B',
-    '#14171A',
-    '#0F1A20',
-    '#0F1C25',
-    '#10232E',
-    '#0E202A',
-    '#0E1E27',
-    '#0E1D26',
-    '#0F202B',
-    '#11232F',
-    '#102632',
-    '#102530',
-    '#122430',
-    '#0F1B21',
-    '#0F212C',
-    '#0E1F29',
-    '#112531',
-    '#0F2734',
-    '#0F2835',
-    '#0D1B23',
-    '#0F1A21',
-    '#0F1A23',
-    '#0F1D27',
-    '#0F222D',
-    '#102430',
-    '#102531',
-    '#10222E',
-    '#0F232D',
-    '#0E2633',
-    '#0E2734',
-    '#0F2834',
-    '#0E2835',
-    '#0F2633',
-    '#0F2532',
-    '#0E1A22',
-    '#0D1C24',
-    '#0F2735',
-    '#0F2937',
-    '#102A38',
-    '#112938',
-    '#102A39',
-    '#0F2A38',
-    '#102836',
-    '#0E1B23',
-    '#0F2938',
-    '#102A3A',
-    '#102D3D',
-    '#0F3040',
-    '#102D3E',
-    '#0F2E3E',
-    '#112C3B',
-    '#102B3B',
-    '#102B3A',
-    '#102D3C',
-    '#0F2A39',
-    '#0F2634',
-    '#0E2029',
-    '#0E1A21',
-    '#0F2B39',
-    '#0F2D3D',
-    '#0F2F40',
-    '#0E3142',
-    '#113445',
-    '#122431',
-    '#102E3E',
-    '#0F3345',
-    '#0E2F40',
-    '#0F3143',
-    '#102C3C',
-    '#0F2B3A',
-    '#0F1F28',
-    '#0F3344',
-    '#113548',
-    '#113C51',
-    '#144258',
-    '#103A4E',
-    '#103A4F',
-    '#103547',
-    '#10364A',
-    '#103649',
-    '#0F3448',
-    '#102C3A',
-    '#0F2836',
-    '#103447',
-    '#0F384C',
-    '#123F55',
-    '#15445A',
-    '#133F55',
-    '#103B50',
-    '#113E54',
-    '#103446',
-    '#0F3A4F',
-    '#0F3548',
-    '#0D3142',
-    '#102C3B',
-    '#0E2937',
-    '#103D52',
-    '#0E3544',
-    '#184C65',
-    '#154760',
-    '#14435B',
-    '#15465F',
-    '#124159',
-    '#0F3D53',
-    '#103C51',
-    '#0F3447',
-    '#0E3243',
-    '#113143',
-    '#113D53',
-    '#184B64',
-    '#184D67',
-    '#184C66',
-    '#174A63',
-    '#15455C',
-    '#13425A',
-    '#14445A',
-    '#10384C',
-    '#0E3446',
-    '#10181E',
-    '#103243',
-    '#0F384D',
-    '#14455C',
-    '#164761',
-    '#164C66',
-    '#1D627D',
-    '#12425A',
-    '#164A63',
-    '#14465D',
-    '#13435A',
-    '#0A2B38',
-    '#0F3446',
-    '#0D2F40',
-    '#0D2F3F',
-    '#0F2531',
-    '#102937',
-    '#10384B',
-    '#0F3649',
-    '#184E68',
-    '#1A5472',
-    '#184D68',
-    '#154A63',
-    '#19506B',
-    '#19536F',
-    '#1A4F69',
-    '#144760',
-    '#114058',
-    '#0E3A4F',
-    '#0E3547',
-    '#0C3042',
-    '#0E1B24',
-    '#11222C',
-    '#154C65',
-    '#1A5776',
-    '#1B5675',
-    '#113847',
-    '#1A5371',
-    '#194E68',
-    '#0E2D3D',
-    '#112D3B',
-    '#113D52',
-    '#18516D',
-    '#1A5979',
-    '#1B5878',
-    '#19526E',
-    '#1A526E',
-    '#13435B',
-    '#0F3E55',
-    '#0B374C',
-    '#0E3448',
-    '#0D2E3F',
-    '#0F2B3B',
-    '#112E3E',
-    '#113B50',
-    '#15465D',
-    '#1A526F',
-    '#1E5E81',
-    '#1D5B7B',
-    '#1A5777',
-    '#154456',
-    '#113949',
-    '#0D394E',
-    '#0F3549',
-    '#0F2C3B',
-    '#0E2733',
-    '#112E3D',
-    '#123D52',
-    '#10394C',
-    '#1B5674',
-    '#1A5370',
-    '#144861',
-    '#104058',
-    '#104159',
-    '#0E384C',
-    '#0D2D3D',
-    '#0E2533',
-    '#112C3A',
-    '#1B5979',
-    '#1B5C7D',
-    '#1A5675',
-    '#104057',
-    '#0F3C51',
-    '#11425A',
-    '#0E394D',
-    '#0C3243',
-    '#0E2735',
-    '#112F3E',
-    '#134158',
-    '#1D5E7F',
-    '#1D6083',
-    '#1C5877',
-    '#1A5573',
-    '#184D66',
-    '#164962',
-    '#0F3D54',
-    '#0E3D53',
-    '#0E3447',
-    '#0F2A3A',
-    '#0F2936',
-    '#101F28',
-    '#103040',
-    '#124056',
-    '#164E69',
-    '#144B64',
-    '#164D66',
-    '#0F3E54',
-    '#0E3B51',
-    '#0D3346',
-    '#0E1F27',
-    '#124158',
-    '#164961',
-    '#0E3C52',
-    '#19506C',
-    '#0F2C3C',
-    '#0E3244',
-    '#0E2A39',
-    '#0E2938',
-    '#113040',
-    '#134057',
-    '#1A5471',
-    '#154B63',
-    '#1C597A',
-    '#164760',
-    '#10374B',
-    '#0E374C',
-    '#0E384D',
-    '#11242F',
-    '#10394D',
-    '#18526E',
-    '#154B65',
-    '#103F55',
-    '#0D3345',
-    '#102532',
-    '#102029',
-    '#113142',
-    '#1B5973',
-    '#1A516B',
-    '#1C5979',
-    '#1C5A7A',
-    '#184A65',
-    '#164C65',
-    '#0D3041',
-    '#123142',
-    '#123E54',
-    '#1B5877',
-    '#1A5574',
-    '#1C5878',
-    '#13435C',
-    '#0F374B',
-    '#0C3143',
-    '#112F40',
-    '#123C51',
-    '#174E68',
-    '#1D5C7D',
-    '#14465F',
-    '#0F3F56',
-    '#0B3041',
-    '#123243',
-    '#15435B',
-    '#19516D',
-    '#1D5D7E',
-    '#1C5C7D',
-    '#184F69',
-    '#11374B',
-    '#103E54',
-    '#0E3143',
-    '#0F2D3C',
-    '#11242E',
-    '#133445',
-    '#1A5674',
-    '#1D6184',
-    '#1F658B',
-    '#0D3A50',
-    '#0C374B',
-    '#154862',
-    '#164B64',
-    '#154961',
-    '#0D384D',
-    '#102631',
-    '#113242',
-    '#134259',
-    '#185270',
-    '#1D6386',
-    '#1E678C',
-    '#1C5978',
-    '#0D3549',
-    '#0F2632',
-    '#184961',
-    '#1D5E80',
-    '#1E6488',
-    '#1F678D',
-    '#1E5B7C',
-    '#164862',
-    '#19526D',
-    '#113C52',
-    '#15455E',
-    '#0F2F3F',
-    '#144259',
-    '#194D67',
-    '#1D6991',
-    '#195777',
-    '#19516C',
-    '#103F56',
-    '#144660',
-    '#0D2E3E',
-    '#10212A',
-    '#113141',
-    '#16455C',
-    '#1D5B7C',
-    '#1F6589',
-    '#1E668C',
-    '#1E5F81',
-    '#0F3B50',
-    '#0D3244',
-    '#164A64',
-    '#184E69',
-    '#0E364A',
-    '#0E2E3E',
-    '#10222B',
-    '#19475E',
-    '#1B5A7B',
-    '#1E5D7F',
-    '#1E678D',
-    '#1E6184',
-    '#19506A',
-    '#1B5370',
-    '#1B5573',
-    '#0E3041',
-    '#122E3E',
-    '#16455B',
-    '#195370',
-    '#1D6489',
-    '#1D6B93',
-    '#164A65',
-    '#154A64',
-    '#1A5572',
-    '#1D6082',
-    '#1F6286',
-    '#1D6C94',
-    '#1E709A',
-    '#174A65',
-    '#1B526F',
-    '#1E6589',
-    '#1D6384',
-    '#0D3143',
-    '#0E2F3F',
-    '#174760',
-    '#1F6487',
-    '#1D668C',
-    '#0D2F41',
-    '#103B4F',
-    '#1C5C7E',
-    '#1F688F',
-    '#1C5B7C',
-    '#164D68',
-    '#1D6285',
-    '#0D364A',
-    '#1D5A7A',
-    '#1E6990',
-    '#1D6488',
-    '#18516B',
-    '#1A506B',
-    '#0E3B50',
-    '#0E3548',
-    '#124259',
-    '#13455C',
-    '#14485F',
-    '#1E5C7D',
-    '#122D3C',
-    '#1E6E98',
-    '#1E6A91',
-    '#1E6286',
-    '#1E6C95',
-    '#1D6990',
-    '#101F29',
-    '#174A62',
-    '#10394E',
-    '#1D6D96',
-    '#1E688E',
-    '#1D6E97',
-    '#1E6C94',
-    '#0E394E',
-    '#112B39',
-    '#195270',
-    '#1E668B',
-    '#1E6386',
-    '#1D6385',
-    '#0C3142',
-    '#1E6083',
-    '#1E729C',
-    '#1F709A',
-    '#1E6F98',
-    '#1D5F81',
-    '#1F688D',
-    '#1C6488',
-    '#1D6588',
-    '#1C6A93',
-    '#1E658B',
-    '#1F6C95',
-    '#0D3C52',
-    '#1C6385',
-    '#1E5F82',
-    '#0E3D54',
-    '#0F3244',
-    '#18485F',
-    '#1E6991',
-    '#1C5B7B',
-    '#1F6082',
-    '#0F3346',
-    '#18536F',
-    '#114056',
-    '#1D6B92',
-    '#1B5776',
-    '#0F3C52',
-    '#1E6890',
-    '#1F688E',
-    '#0C394E',
-    '#0F1D25',
-    '#1F6386',
-    '#1E688D',
-    '#1F6488',
-    '#20668C',
-    '#1D5978',
-    '#0F3D52',
-    '#0F1E26',
-    '#13465F',
-    '#0D374C',
-    '#1B5C7C',
-    '#0E1A23',
-    '#0F374A',
-    '#1B5574',
-    '#0F394C',
-    '#0E2A38',
-    '#102A37',
-    '#18506B',
-    '#1E5A7A',
-    '#0F3245',
-    '#0E2E3F',
-    '#1E678E',
-    '#1C5D7E',
-    '#1A5A7A',
-    '#0E2837',
-    '#102733',
-    '#0F3B51',
-    '#15475E',
-    '#1E6B93',
-    '#1E648A',
-    '#194961',
-    '#0F3A4E',
-    '#0E1D25',
-    '#194F69',
-    '#103345',
-    '#0F394D',
-    '#102B39',
-    '#103E55',
-    '#1B5572',
-    '#164861',
-    '#174861',
-    '#113B4F',
-    '#102936',
-    '#0F3041',
-    '#174961',
-    '#113E53',
-    '#134056',
-    '#124057',
-    '#194B63',
-    '#0E364B',
-    '#15445B',
-    '#16475E',
-    '#102F3F',
-    '#16485F',
-    '#0F2E3D',
-    '#101920',
-    '#12222C',
-    '#122C3B',
-    '#144157',
-    '#123B50',
-    '#16465D',
-    '#184960',
-    '#112B3A',
-    '#12232F',
-    '#132430',
-    '#113344',
-    '#11394C',
-    '#113649',
-    '#11364A',
-    '#133F56',
-    '#121D25',
-    '#112733',
-    '#112A38',
-    '#0F1F2A',
-    '#113447',
-    '#113A4E',
-    '#0F222C',
-    '#13222B',
-    '#112836',
-    '#102F3E',
-    '#113243',
-    '#123445',
-    '#12374B',
-    '#121E26',
-    '#122531',
-    '#11303F',
-    '#0D1D25',
-    '#102835',
-    '#112834',
-    '#101C23',
-    '#111C23',
-    '#12212B',
-    '#11222D',
-    '#0E1B22',
-    '#0E1D27',
-    '#121C22',
-    '#12202A',
-    '#101A20',
-    '#13191E',
-    '#111E28',
-    '#11212D',
-    '#0F1B24',
-    '#0F1C23',
-    '#13181D',
-    '#15171A',
-    '#121D23',
-    '#121F27',
-    '#111E27',
-    '#101B22',
-    '#121F28',
-    '#111E26',
-    '#101D24',
-    '#111C22',
-    '#12161E',
-    '#101925',
-    '#121E2D',
-    '#112033',
-    '#111E2F',
-    '#0F1B29',
-    '#0F1A28',
-    '#101B2A',
-    '#0E1A27',
-    '#101C2B',
-    '#111D2D',
-    '#111D2B',
-    '#0F1B28',
-    '#101923',
-    '#13161D',
-    '#13161C',
-    '#0F1A26',
-    '#101E2F',
-    '#112235',
-    '#102031',
-    '#0F1B2A',
-    '#112031',
-    '#102032',
-    '#101D2E',
-    '#121F2F',
-    '#112133',
-    '#101E30',
-    '#101F30',
-    '#102336',
-    '#101B2C',
-    '#0F1C2B',
-    '#111E2E',
-    '#0F2134',
-    '#102236',
-    '#0F2133',
-    '#101F31',
-    '#0F2438',
-    '#102337',
-    '#102235',
-    '#102133',
-    '#11171E',
-    '#101F2F',
-    '#102030',
-    '#102234',
-    '#102132',
-    '#12181F',
-    '#0F1A25',
-    '#0F2135',
-    '#0F1F30',
-    '#0F1C2D',
-    '#101D2C',
-    '#0F2033',
-    '#0E2338',
-    '#0F2237',
-    '#0F2236',
-    '#0B243B',
-    '#0D2338',
-    '#0E1A26',
-    '#0F1D2E',
-    '#0F2032',
-    '#0D2339',
-    '#0B253F',
-    '#0A253F',
-    '#0A253E',
-    '#0C2439',
-    '#0E1925',
-    '#0E2135',
-    '#0F2235',
-    '#0A243A',
-    '#08253E',
-    '#09253E',
-    '#0A263F',
-    '#0A243C',
-    '#0B233B',
-    '#0E1A28',
-    '#0D1A26',
-    '#09253F',
-    '#0A2743',
-    '#0B2844',
-    '#0B2641',
-    '#0A2744',
-    '#0A2844',
-    '#0B2743',
-    '#092745',
-    '#0F2337',
-    '#101D2D',
-    '#092743',
-    '#092846',
-    '#0E2B4C',
-    '#102E4F',
-    '#0E2C4D',
-    '#0B2A49',
-    '#082947',
-    '#0D2B4B',
-    '#0C2A4A',
-    '#092946',
-    '#082845',
-    '#0C2B4B',
-    '#0F2D4E',
-    '#103051',
-    '#133257',
-    '#0E2D4E',
-    '#143156',
-    '#112F51',
-    '#0B243A',
-    '#082744',
-    '#092844',
-    '#123054',
-    '#143359',
-    '#173A64',
-    '#183F6E',
-    '#173F6D',
-    '#153961',
-    '#163962',
-    '#133358',
-    '#15345B',
-    '#14345A',
-    '#102F50',
-    '#0A2948',
-    '#082844',
-    '#092641',
-    '#16375F',
-    '#193C69',
-    '#174170',
-    '#173E6B',
-    '#163A63',
-    '#173D69',
-    '#183D6A',
-    '#15365E',
-    '#112E50',
-    '#0A2A49',
-    '#082743',
-    '#0E1927',
-    '#173C68',
-    '#13487E',
-    '#164476',
-    '#174375',
-    '#193F6F',
-    '#173B66',
-    '#163B65',
-    '#082A48',
-    '#0A2641',
-    '#09243C',
-    '#174171',
-    '#14477C',
-    '#124980',
-    '#14487F',
-    '#174374',
-    '#15467B',
-    '#184172',
-    '#17406F',
-    '#184070',
-    '#163C67',
-    '#16355D',
-    '#123256',
-    '#0E1B29',
-    '#0F1923',
-    '#113052',
-    '#184274',
-    '#164579',
-    '#13477C',
-    '#193E6D',
-    '#0A243E',
-    '#0B233A',
-    '#0D1A29',
-    '#0B2742',
-    '#17365E',
-    '#163860',
-    '#124A84',
-    '#095191',
-    '#114A83',
-    '#0D4D8A',
-    '#0C4D8C',
-    '#104B85',
-    '#15477E',
-    '#174477',
-    '#183862',
-    '#0A233A',
-    '#092947',
-    '#09243D',
-    '#173963',
-    '#194173',
-    '#085396',
-    '#085394',
-    '#114B87',
-    '#144983',
-    '#094F8E',
-    '#075090',
-    '#0F4C89',
-    '#215287',
-    '#0E1A29',
-    '#184376',
-    '#0C4D8B',
-    '#07549A',
-    '#0A4E8D',
-    '#0F4C88',
-    '#0A4E8C',
-    '#174273',
-    '#193C6A',
-    '#0B2948',
-    '#0B2C4B',
-    '#0C4E8D',
-    '#1259A4',
-    '#0C579E',
-    '#0D4D8B',
-    '#095397',
-    '#085397',
-    '#085295',
-    '#144880',
-    '#173861',
-    '#15335A',
-    '#0F2C4D',
-    '#0C2949',
-    '#0B4E8D',
-    '#08559C',
-    '#07508F',
-    '#154578',
-    '#17365F',
-    '#122F53',
-    '#111D2C',
-    '#092A48',
-    '#08559D',
-    '#08559E',
-    '#0C56A1',
-    '#164271',
-    '#163E6A',
-    '#194071',
-    '#082642',
-    '#0F1E30',
-    '#0D2D4D',
-    '#114C87',
-    '#0E59A3',
-    '#135BA6',
-    '#085498',
-    '#085497',
-    '#095192',
-    '#0E4D8B',
-    '#0C4E8A',
-    '#134982',
-    '#17457B',
-    '#121F2E',
-    '#183E6C',
-    '#153E69',
-    '#07508E',
-    '#173F6C',
-    '#193D6B',
-    '#112D4F',
-    '#0A243B',
-    '#072946',
-    '#111E2D',
-    '#0B2740',
-    '#10497F',
-    '#17406E',
-    '#084F8D',
-    '#104A80',
-    '#0E2E4F',
-    '#143358',
-    '#16365D',
-    '#0A2742',
-    '#13477B',
-    '#154474',
-    '#104C86',
-    '#095291',
-    '#0B4F8E',
-    '#114A80',
-    '#095090',
-    '#075296',
-    '#163760',
-    '#2D6DB5',
-    '#0C2843',
-    '#0C233A',
-    '#153A62',
-    '#14467A',
-    '#075498',
-    '#085293',
-    '#09263F',
-    '#122030',
-    '#09559D',
-    '#0F4B83',
-    '#08549A',
-    '#14375D',
-    '#085499',
-    '#075499',
-    '#0A243D',
-    '#143E68',
-    '#10497E',
-    '#074F8E',
-    '#085496',
-    '#0C58A3',
-    '#065499',
-    '#085190',
-    '#0A2B4A',
-    '#104C88',
-    '#0D4F8E',
-    '#0F58A2',
-    '#0B569B',
-    '#0D58A1',
-    '#134A81',
-    '#09559C',
-    '#0A5293',
-    '#114B86',
-    '#0D2C4C',
-    '#103255',
-    '#16457A',
-    '#074F8C',
-    '#07559C',
-    '#185DA9',
-    '#1D61AD',
-    '#175CA8',
-    '#16406D',
-    '#153C65',
-    '#0E243A',
-    '#144679',
-    '#085192',
-    '#1A5EAC',
-    '#1D61AE',
-    '#11497F',
-    '#12487E',
-    '#0C243C',
-    '#123155',
-    '#0F59A3',
-    '#1B5FAB',
-    '#1E61AD',
-    '#145CA4',
-    '#0E599F',
-    '#11497E',
-    '#094F8D',
-    '#15345A',
-    '#134A85',
-    '#165CA8',
-    '#2263AF',
-    '#124466',
-    '#0A518F',
-    '#08569D',
-    '#16416F',
-    '#0B2B4A',
-    '#124A83',
-    '#0C57A2',
-    '#1E60AD',
-    '#1E62AE',
-    '#165DA8',
-    '#1059A4',
-    '#15406C',
-    '#0A4F8E',
-    '#12365A',
-    '#0A5191',
-    '#16355C',
-    '#1C5EAB',
-    '#155CA7',
-    '#085292',
-    '#174478',
-    '#153258',
-    '#111F2F',
-    '#174272',
-    '#1159A5',
-    '#1C5EAC',
-    '#2F74BB',
-    '#0C58A2',
-    '#0D59A3',
-    '#14477D',
-    '#132F53',
-    '#155BA6',
-    '#195FAA',
-    '#2366B1',
-    '#2967B2',
-    '#14477E',
-    '#1B5EAB',
-    '#175DA8',
-    '#0F4C86',
-    '#065090',
-    '#1C5FAC',
-    '#185CA8',
-    '#0D58A3',
-    '#0C4E8C',
-    '#134981',
-    '#14416D',
-    '#0F5AA5',
-    '#1F63AF',
-    '#114B88',
-    '#09508E',
-    '#0A569D',
-    '#195DAA',
-    '#0F1D2F',
-    '#1059A2',
-    '#0E599E',
-    '#2063AF',
-    '#1F63AE',
-    '#1A5EAA',
-    '#0C57A0',
-    '#195EAA',
-    '#1A5EA9',
-    '#0E4E8A',
-    '#12487D',
-    '#185DAA',
-    '#175EAA',
-    '#0A508E',
-    '#1559A6',
-    '#0E58A3',
-    '#095399',
-    '#0B4E8B',
-    '#0B569F',
-    '#0C57A1',
-    '#2967B1',
-    '#2365B0',
-    '#2163AE',
-    '#1A5DAA',
-    '#195EAB',
-    '#1E5FAC',
-    '#2564AF',
-    '#2767B1',
-    '#2766B1',
-    '#0D5A9F',
-    '#2062AE',
-    '#1F61AD',
-    '#195FAB',
-    '#0D4E8D',
-    '#173760',
-    '#111D2E',
-    '#09518F',
-    '#1A5FAC',
-    '#135BA7',
-    '#085291',
-    '#183761',
-    '#0B2845',
-    '#113457',
-    '#075393',
-    '#185EA9',
-    '#2B69B3',
-    '#2A67B2',
-    '#2867B1',
-    '#155DA8',
-    '#135CA6',
-    '#135AA5',
-    '#114980',
-    '#2566B1',
-    '#2064AF',
-    '#2364AF',
-    '#13365B',
-    '#154475',
-    '#08549B',
-    '#164373',
-    '#085392',
-    '#144576',
-    '#12497E',
-    '#0E5392',
-    '#135BA3',
-    '#0C5395',
-    '#0C5291',
-    '#0E579C',
-    '#0E5290',
-    '#134C83',
-    '#2163AC',
-    '#195CA6',
-    '#0D4E8C',
-    '#082945',
-    '#133256',
-    '#0E2F50',
-    '#105AA6',
-    '#134677',
-    '#144475',
-    '#145BA7',
-    '#154270',
-    '#1D60AD',
-    '#09569B',
-    '#09243E',
-    '#134A86',
-    '#0E59A4',
-    '#0A4E8B',
-    '#0E4B83',
-    '#1D5EAC',
-    '#101C2A',
-    '#134A84',
-    '#0E518F',
-    '#145CA7',
-    '#0E5699',
-    '#145BA5',
-    '#095292',
-    '#15416E',
-    '#153D67',
-    '#153F6B',
-    '#125AA5',
-    '#16406E',
-    '#0E1B27',
-    '#0D4F8C',
-    '#0F58A3',
-    '#114A82',
-    '#09569C',
-    '#0C2339',
-    '#0E1B28',
-    '#0D59A4',
-    '#07559D',
-    '#08569E',
-    '#095190',
-    '#0B253E',
-    '#0C2B49',
-    '#2264AF',
-    '#09549A',
-    '#09569F',
-    '#163D68',
-    '#0C263F',
-    '#143960',
-    '#183A65',
-    '#075496',
-    '#0C579F',
-    '#085191',
-    '#102438',
-    '#075295',
-    '#082946',
-    '#102437',
-    '#0C2642',
-    '#101C29',
-    '#0C253E',
-    '#15355C',
-    '#0B2E4D',
-    '#0F3253',
-    '#154577',
-    '#16335B',
-    '#0F1925',
-    '#0C2742',
-    '#0B2946',
-    '#0E2C4B',
-    '#0E2B48',
-    '#0E2237',
-    '#102237',
-    '#0B253D',
-    '#0A2946',
-    '#0C2841',
-    '#0D2A47',
-    '#0C2C4A',
-    '#08253F',
-    '#08243D',
-    '#111C2B',
-    '#0C2844',
-    '#0C2945',
-    '#0D243A',
-    '#122134',
-    '#0B2642',
-    '#113154',
-    '#113255',
-    '#0A2642',
-    '#0A2945',
-    '#0B263F',
-    '#0D2E4E',
-    '#0F1E2E',
-    '#0A2845',
-    '#0D2439',
-    '#0F1A29',
-    '#101C2E',
-    '#111923',
-    '#13181F',
-    '#111D2F',
-    '#111F30',
-    '#121E30',
-    '#121E2E',
-    '#101B27',
-    '#101A27',
-    '#13171F',
-  ];
-
-  // let randX = getRandomInt(0, x);
-  // let randY = getRandomInt(0, y);
-  // let randIndex = randY * xCount + randX;
-
-  return colors[(y*xCount + x) % colors.length];
-}

+ 3 - 3
public/app/core/components/form_dropdown/form_dropdown.ts

@@ -1,8 +1,8 @@
 import _ from 'lodash';
 import coreModule from '../../core_module';
 
-function typeaheadMatcher(item) {
-  var str = this.query;
+function typeaheadMatcher(this: any, item) {
+  let str = this.query;
   if (str === '') {
     return true;
   }
@@ -36,7 +36,7 @@ export class FormDropdownCtrl {
   startOpen: any;
   debounce: number;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private $scope, $element, private $sce, private templateSrv, private $q) {
     this.inputElement = $element.find('input').first();
     this.linkElement = $element.find('a').first();

+ 63 - 41
public/app/core/components/grafana_app.ts

@@ -50,7 +50,7 @@ export class GrafanaCtrl {
 
     $rootScope.onAppEvent = function(name, callback, localScope) {
       const unbind = $rootScope.$on(name, callback);
-      var callerScope = this;
+      let callerScope = this;
       if (callerScope.$id === 1 && !localScope) {
         console.log('warning rootScope onAppEvent called without localscope');
       }
@@ -69,18 +69,44 @@ export class GrafanaCtrl {
   }
 }
 
+function setViewModeBodyClass(body, mode, sidemenuOpen: boolean) {
+  body.removeClass('view-mode--tv');
+  body.removeClass('view-mode--kiosk');
+  body.removeClass('view-mode--inactive');
+
+  switch (mode) {
+    case 'tv': {
+      body.removeClass('sidemenu-open');
+      body.addClass('view-mode--tv');
+      break;
+    }
+    // 1 & true for legacy states
+    case 1:
+    case true: {
+      body.removeClass('sidemenu-open');
+      body.addClass('view-mode--kiosk');
+      break;
+    }
+    default: {
+      body.toggleClass('sidemenu-open', sidemenuOpen);
+    }
+  }
+}
+
 /** @ngInject */
 export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope, $location) {
   return {
     restrict: 'E',
     controller: GrafanaCtrl,
     link: (scope, elem) => {
-      var sidemenuOpen;
+      let sidemenuOpen;
       const body = $('body');
 
       // see https://github.com/zenorocha/clipboard.js/issues/155
       $.fn.modal.Constructor.prototype.enforceFocus = function() {};
 
+      $('.preloader').remove();
+
       sidemenuOpen = scope.contextSrv.sidemenu;
       body.toggleClass('sidemenu-open', sidemenuOpen);
 
@@ -98,7 +124,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
       });
 
       scope.$watch(() => playlistSrv.isPlaying, function(newValue) {
-        elem.toggleClass('playlist-active', newValue === true);
+        elem.toggleClass('view-mode--playlist', newValue === true);
       });
 
       // check if we are in server side render
@@ -108,7 +134,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
 
       // tooltip removal fix
       // manage page classes
-      var pageClass;
+      let pageClass;
       scope.$on('$routeChangeSuccess', function(evt, data) {
         if (pageClass) {
           body.removeClass(pageClass);
@@ -127,17 +153,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
         $('#tooltip, .tooltip').remove();
 
         // check for kiosk url param
-        if (data.params.kiosk) {
-          appEvents.emit('toggle-kiosk-mode');
-        }
-
-        // check for 'inactive' url param for clean looks like kiosk, but with title
-        if (data.params.inactive) {
-          body.addClass('user-activity-low');
-
-          // for some reason, with this class it looks cleanest
-          body.addClass('sidemenu-open');
-        }
+        setViewModeBodyClass(body, data.params.kiosk, sidemenuOpen);
 
         // close all drops
         for (const drop of Drop.drops) {
@@ -146,15 +162,37 @@ 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', options => {
+        const search = $location.search();
+
+        if (options && options.exit) {
+          search.kiosk = 1;
+        }
+
+        switch (search.kiosk) {
+          case 'tv': {
+            search.kiosk = 1;
+            appEvents.emit('alert-success', ['Press ESC to exit Kiosk mode']);
+            break;
+          }
+          case 1:
+          case true: {
+            delete search.kiosk;
+            break;
+          }
+          default: {
+            search.kiosk = 'tv';
+          }
+        }
+
+        $location.search(search);
+        setViewModeBodyClass(body, search.kiosk, sidemenuOpen);
       });
 
       // handle in active view state class
-      var lastActivity = new Date().getTime();
-      var activeUser = true;
-      const inActiveTimeLimit = 60 * 1000;
-      var sidemenuHidden = false;
+      let lastActivity = new Date().getTime();
+      let activeUser = true;
+      const inActiveTimeLimit = 60 * 5000;
 
       function checkForInActiveUser() {
         if (!activeUser) {
@@ -167,15 +205,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
 
         if (new Date().getTime() - lastActivity > inActiveTimeLimit) {
           activeUser = false;
-          body.addClass('user-activity-low');
-          // hide sidemenu
-          if (sidemenuOpen) {
-            sidemenuHidden = true;
-            body.removeClass('sidemenu-open');
-            $timeout(function() {
-              $rootScope.$broadcast('render');
-            }, 100);
-          }
+          body.addClass('view-mode--inactive');
+          body.removeClass('sidemenu-open');
         }
       }
 
@@ -183,17 +214,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
         lastActivity = new Date().getTime();
         if (!activeUser) {
           activeUser = true;
-          body.removeClass('user-activity-low');
-
-          // restore sidemenu
-          if (sidemenuHidden) {
-            sidemenuHidden = false;
-            body.addClass('sidemenu-open');
-            appEvents.emit('toggle-inactive-mode');
-            $timeout(function() {
-              $rootScope.$broadcast('render');
-            }, 100);
-          }
+          body.removeClass('view-mode--inactive');
+          body.toggleClass('sidemenu-open', sidemenuOpen);
         }
       }
 

+ 10 - 10
public/app/core/components/json_explorer/helpers.ts

@@ -21,7 +21,7 @@ export function isObject(value: any): boolean {
  * From http://stackoverflow.com/a/332429
  *
 */
-export function getObjectName(object: Object): string {
+export function getObjectName(object: object): string {
   if (object === undefined) {
     return '';
   }
@@ -44,7 +44,7 @@ export function getObjectName(object: Object): string {
 /*
  * Gets type of an object. Returns "null" for null objects
 */
-export function getType(object: Object): string {
+export function getType(object: object): string {
   if (object === null) {
     return 'null';
   }
@@ -54,7 +54,7 @@ export function getType(object: Object): string {
 /*
  * 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 {
   const type = getType(object);
 
   if (type === 'null' || type === 'undefined') {
@@ -79,15 +79,15 @@ export function getValuePreview(object: Object, value: string): string {
 /*
  * Generates inline preview for a JavaScript object
 */
-export function getPreview(object: string): string {
-  let value = '';
-  if (isObject(object)) {
-    value = getObjectName(object);
-    if (Array.isArray(object)) {
-      value += '[' + object.length + ']';
+let value = '';
+export function getPreview(obj: object): string {
+  if (isObject(obj)) {
+    value = getObjectName(obj);
+    if (Array.isArray(obj)) {
+      value += '[' + obj.length + ']';
     }
   } else {
-    value = getValuePreview(object, object);
+    value = getValuePreview(obj, obj.toString());
   }
   return value;
 }

+ 4 - 4
public/app/core/components/layout_selector/layout_selector.ts

@@ -15,7 +15,7 @@ const template = `
 export class LayoutSelectorCtrl {
   mode: string;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private $rootScope) {
     this.mode = store.get('grafana.list.layout.mode') || 'grid';
   }
@@ -33,7 +33,7 @@ export class LayoutSelectorCtrl {
   }
 }
 
-/** @ngInject **/
+/** @ngInject */
 export function layoutSelector() {
   return {
     restrict: 'E',
@@ -45,14 +45,14 @@ export function layoutSelector() {
   };
 }
 
-/** @ngInject **/
+/** @ngInject */
 export function layoutMode($rootScope) {
   return {
     restrict: 'A',
     scope: {},
     link: function(scope, elem) {
       const layout = store.get('grafana.list.layout.mode') || 'grid';
-      var className = 'card-list-layout-' + layout;
+      let className = 'card-list-layout-' + layout;
       elem.addClass(className);
 
       $rootScope.onAppEvent(

+ 1 - 1
public/app/core/components/manage_dashboards/manage_dashboards.ts

@@ -14,7 +14,7 @@ class Query {
 }
 
 export class ManageDashboardsCtrl {
-  public sections: any[];
+  sections: any[];
 
   query: Query;
   navModel: any;

+ 1 - 1
public/app/core/components/query_part/query_part.ts

@@ -74,7 +74,7 @@ export class QueryPart {
       return;
     }
 
-    var text = this.def.type + '(';
+    let text = this.def.type + '(';
     text += this.params.join(', ');
     text += ')';
     this.text = text;

+ 5 - 5
public/app/core/components/query_part/query_part_editor.ts

@@ -33,7 +33,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
 
       $scope.partActions = [];
 
-      function clickFuncParam(paramIndex) {
+      function clickFuncParam(this: any, paramIndex) {
         /*jshint validthis:true */
         const $link = $(this);
         const $input = $link.next();
@@ -53,7 +53,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
         }
       }
 
-      function inputBlur(paramIndex) {
+      function inputBlur(this: any, paramIndex) {
         /*jshint validthis:true */
         const $input = $(this);
         const $link = $input.prev();
@@ -72,14 +72,14 @@ export function queryPartEditorDirective($compile, templateSrv) {
         $link.show();
       }
 
-      function inputKeyPress(paramIndex, e) {
+      function inputKeyPress(this: any, paramIndex, e) {
         /*jshint validthis:true */
         if (e.which === 13) {
           inputBlur.call(this, paramIndex);
         }
       }
 
-      function inputKeyDown() {
+      function inputKeyDown(this: any) {
         /*jshint validthis:true */
         this.style.width = (3 + this.value.length) * 8 + 'px';
       }
@@ -91,7 +91,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
 
         const typeaheadSource = function(query, callback) {
           if (param.options) {
-            var options = param.options;
+            let options = param.options;
             if (param.type === 'int') {
               options = _.map(options, function(val) {
                 return val.toString();

+ 0 - 38
public/app/core/components/scroll/scroll.ts

@@ -1,7 +1,6 @@
 import $ from 'jquery';
 import baron from 'baron';
 import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
 
 const scrollBarHTML = `
 <div class="baron__track">
@@ -39,43 +38,6 @@ export function geminiScrollbar() {
 
       const scrollbar = baron(scrollParams);
 
-      let lastPos = 0;
-
-      appEvents.on(
-        'dash-scroll',
-        evt => {
-          if (evt.restore) {
-            elem[0].scrollTop = lastPos;
-            return;
-          }
-
-          lastPos = elem[0].scrollTop;
-
-          if (evt.animate) {
-            elem.animate({ scrollTop: evt.pos }, 500);
-          } else {
-            elem[0].scrollTop = evt.pos;
-          }
-        },
-        scope
-      );
-
-      // force updating dashboard width
-      appEvents.on('toggle-sidemenu', forceUpdate, scope);
-      appEvents.on('toggle-sidemenu-hidden', forceUpdate, scope);
-      appEvents.on('toggle-view-mode', forceUpdate, scope);
-      appEvents.on('toggle-kiosk-mode', forceUpdate, scope);
-      appEvents.on('toggle-inactive-mode', forceUpdate, scope);
-
-      function forceUpdate() {
-        scrollbar.scroll();
-      }
-
-      scope.$on('$routeChangeSuccess', () => {
-        lastPos = 0;
-        elem[0].scrollTop = 0;
-      });
-
       scope.$on('$destroy', () => {
         scrollbar.dispose();
       });

+ 21 - 21
public/app/core/components/sql_part/sql_part_editor.ts

@@ -2,7 +2,7 @@ import _ from 'lodash';
 import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 
-let template = `
+const template = `
 <div class="dropdown cascade-open">
 <a ng-click="showActionsMenu()" class="query-part-name pointer dropdown-toggle" data-toggle="dropdown">{{part.label}}</a>
 <span>{{part.def.wrapOpen}}</span><span class="query-part-parameters"></span><span>{{part.def.wrapClose}}</span>
@@ -15,7 +15,7 @@ let template = `
 
 /** @ngInject */
 export function sqlPartEditorDirective($compile, templateSrv) {
-  let paramTemplate = '<input type="text" class="hide input-mini"></input>';
+  const paramTemplate = '<input type="text" class="hide input-mini"></input>';
 
   return {
     restrict: 'E',
@@ -26,18 +26,18 @@ export function sqlPartEditorDirective($compile, templateSrv) {
       debounce: '@',
     },
     link: function postLink($scope, elem) {
-      let part = $scope.part;
-      let partDef = part.def;
-      let $paramsContainer = elem.find('.query-part-parameters');
-      let debounceLookup = $scope.debounce;
+      const part = $scope.part;
+      const partDef = part.def;
+      const $paramsContainer = elem.find('.query-part-parameters');
+      const debounceLookup = $scope.debounce;
       let cancelBlur = null;
 
       $scope.partActions = [];
 
-      function clickFuncParam(paramIndex) {
+      function clickFuncParam(this: any, paramIndex) {
         /*jshint validthis:true */
-        let $link = $(this);
-        let $input = $link.next();
+        const $link = $(this);
+        const $input = $link.next();
 
         $input.val(part.params[paramIndex]);
         $input.css('width', $link.width() + 16 + 'px');
@@ -47,7 +47,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
         $input.focus();
         $input.select();
 
-        let typeahead = $input.data('typeahead');
+        const typeahead = $input.data('typeahead');
         if (typeahead) {
           $input.val('');
           typeahead.lookup();
@@ -62,8 +62,8 @@ export function sqlPartEditorDirective($compile, templateSrv) {
 
       function switchToLink($input, paramIndex) {
         /*jshint validthis:true */
-        let $link = $input.prev();
-        let newValue = $input.val();
+        const $link = $input.prev();
+        const newValue = $input.val();
 
         if (newValue !== '' || part.def.params[paramIndex].optional) {
           $link.html(templateSrv.highlightVariablesAsHtml(newValue));
@@ -78,14 +78,14 @@ export function sqlPartEditorDirective($compile, templateSrv) {
         $link.show();
       }
 
-      function inputKeyPress(paramIndex, e) {
+      function inputKeyPress(this: any, paramIndex, e) {
         /*jshint validthis:true */
         if (e.which === 13) {
           switchToLink($(this), paramIndex);
         }
       }
 
-      function inputKeyDown() {
+      function inputKeyDown(this: any) {
         /*jshint validthis:true */
         this.style.width = (3 + this.value.length) * 8 + 'px';
       }
@@ -95,7 +95,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
           return;
         }
 
-        let typeaheadSource = function(query, callback) {
+        const typeaheadSource = function(query, callback) {
           if (param.options) {
             let options = param.options;
             if (param.type === 'int') {
@@ -108,7 +108,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
 
           $scope.$apply(function() {
             $scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then(function(result) {
-              let dynamicOptions = _.map(result, function(op) {
+              const dynamicOptions = _.map(result, function(op) {
                 return op.value;
               });
 
@@ -138,10 +138,10 @@ export function sqlPartEditorDirective($compile, templateSrv) {
           },
         });
 
-        let typeahead = $input.data('typeahead');
+        const typeahead = $input.data('typeahead');
         typeahead.lookup = function() {
           this.query = this.$element.val() || '';
-          let items = this.source(this.query, $.proxy(this.process, this));
+          const items = this.source(this.query, $.proxy(this.process, this));
           return items ? this.process(items) : items;
         };
 
@@ -170,9 +170,9 @@ export function sqlPartEditorDirective($compile, templateSrv) {
             $('<span>' + partDef.separator + '</span>').appendTo($paramsContainer);
           }
 
-          let paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
-          let $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
-          let $input = $(paramTemplate);
+          const paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
+          const $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
+          const $input = $(paramTemplate);
 
           $paramLink.appendTo($paramsContainer);
           $input.appendTo($paramsContainer);

+ 3 - 3
public/app/core/directives/misc.ts

@@ -9,7 +9,7 @@ function tip($compile) {
   return {
     restrict: 'E',
     link: function(scope, elem, attrs) {
-      var _t =
+      let _t =
         '<i class="grafana-tip fa fa-' +
         (attrs.icon || 'question-circle') +
         '" bs-tooltip="\'' +
@@ -125,7 +125,7 @@ function editorCheckbox($compile, $interpolate) {
       const tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
       const label = '<label for="' + scope.$id + model + '" class="checkbox-label">' + text + tip + '</label>';
 
-      var template =
+      let template =
         '<input class="cr1" id="' +
         scope.$id +
         model +
@@ -163,7 +163,7 @@ function gfDropdown($parse, $compile, $timeout) {
         continue;
       }
 
-      var li =
+      let li =
         '<li' +
         (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') +
         '>' +

+ 5 - 5
public/app/core/directives/rebuild_on_change.ts

@@ -2,11 +2,11 @@ import $ from 'jquery';
 import coreModule from '../core_module';
 
 function getBlockNodes(nodes) {
-  var node = nodes[0];
+  let node = nodes[0];
   const endNode = nodes[nodes.length - 1];
-  var blockNodes;
+  let blockNodes;
 
-  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
+  for (let i = 1; node !== endNode && (node = node.nextSibling); i++) {
     if (blockNodes || nodes[i] !== node) {
       if (!blockNodes) {
         blockNodes = $([].slice.call(nodes, 0, i));
@@ -18,7 +18,7 @@ function getBlockNodes(nodes) {
   return blockNodes || nodes;
 }
 
-/** @ngInject **/
+/** @ngInject */
 function rebuildOnChange($animate) {
   return {
     multiElement: true,
@@ -27,7 +27,7 @@ function rebuildOnChange($animate) {
     priority: 600,
     restrict: 'E',
     link: function(scope, elem, attrs, ctrl, transclude) {
-      var block, childScope, previousElements;
+      let block, childScope, previousElements;
 
       function cleanUp() {
         if (previousElements) {

+ 3 - 3
public/app/core/directives/tags.ts

@@ -69,7 +69,7 @@ function bootstrapTagsinput() {
             },
       });
 
-      select.on('itemAdded', function(event) {
+      select.on('itemAdded', event => {
         if (scope.model.indexOf(event.item) === -1) {
           scope.model.push(event.item);
           if (scope.onTagsUpdated) {
@@ -79,7 +79,7 @@ function bootstrapTagsinput() {
         const tagElement = select
           .next()
           .children('span')
-          .filter(function() {
+          .filter(() => {
             return $(this).text() === event.item;
           });
         setColor(event.item, tagElement);
@@ -104,7 +104,7 @@ function bootstrapTagsinput() {
 
           select.tagsinput('removeAll');
 
-          for (var i = 0; i < scope.model.length; i++) {
+          for (let i = 0; i < scope.model.length; i++) {
             select.tagsinput('add', scope.model[i]);
           }
         },

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

@@ -56,7 +56,7 @@ coreModule.filter('noXml', function() {
 /** @ngInject */
 function interpolateTemplateVars(templateSrv) {
   const filterFunc: any = function(text, scope) {
-    var scopedVars;
+    let scopedVars;
     if (scope.ctrl) {
       scopedVars = (scope.ctrl.panel || scope.ctrl.row).scopedVars;
     } else {

+ 2 - 2
public/app/core/jquery_extended.ts

@@ -9,10 +9,10 @@ $.fn.place_tt = (function() {
     offset: 5,
   };
 
-  return function(x, y, opts) {
+  return function(this: any, x, y, opts) {
     opts = $.extend(true, {}, defaults, opts);
 
-    return this.each(function() {
+    return this.each(() => {
       const $tooltip = $(this);
       let width, height;
 

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

@@ -38,7 +38,7 @@ export class NavModelSrv {
   }
 
   getNav(...args) {
-    var children = this.navItems;
+    let children = this.navItems;
     const nav = new NavModel();
 
     for (const id of args) {

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

@@ -1,4 +1,4 @@
-var templates = (<any>require).context('../', true, /\.html$/);
+let templates = (<any>require).context('../', true, /\.html$/);
 templates.keys().forEach(function(key) {
   templates(key);
 });

+ 3 - 3
public/app/core/profiler.ts

@@ -69,7 +69,7 @@ export class Profiler {
 
       // measure digest performance
       const rootDigestStart = window.performance.now();
-      for (var i = 0; i < 30; i++) {
+      for (let i = 0; i < 30; i++) {
         this.$rootScope.$apply();
       }
 
@@ -78,8 +78,8 @@ export class Profiler {
   }
 
   getTotalWatcherCount() {
-    var count = 0;
-    var scopes = 0;
+    let count = 0;
+    let scopes = 0;
     const root = $(document.getElementsByTagName('body'));
 
     const f = function(element) {

+ 1 - 1
public/app/core/services/backend_srv.ts

@@ -43,7 +43,7 @@ export class BackendSrv {
       return;
     }
 
-    var data = err.data || { message: 'Unexpected error' };
+    let data = err.data || { message: 'Unexpected error' };
     if (_.isString(data)) {
       data = { message: data };
     }

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

@@ -7,7 +7,7 @@ export class ImpressionSrv {
 
   addDashboardImpression(dashboardId) {
     const impressionsKey = this.impressionKey(config);
-    var impressions = [];
+    let impressions = [];
     if (store.exists(impressionsKey)) {
       impressions = JSON.parse(store.get(impressionsKey));
       if (!_.isArray(impressions)) {
@@ -28,7 +28,7 @@ export class ImpressionSrv {
   }
 
   getDashboardOpened() {
-    var impressions = store.get(this.impressionKey(config)) || '[]';
+    let impressions = store.get(this.impressionKey(config)) || '[]';
 
     impressions = JSON.parse(impressions);
 

+ 18 - 8
public/app/core/services/keybindingSrv.ts

@@ -77,15 +77,15 @@ export class KeybindingSrv {
 
     appEvents.emit('hide-modal');
 
-    if (!this.modalOpen) {
-      if (this.timepickerOpen) {
-        this.$rootScope.appEvent('closeTimepicker');
-        this.timepickerOpen = false;
-      } else {
-        this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
-      }
-    } else {
+    if (this.modalOpen) {
       this.modalOpen = false;
+      return;
+    }
+
+    if (this.timepickerOpen) {
+      this.$rootScope.appEvent('closeTimepicker');
+      this.timepickerOpen = false;
+      return;
     }
 
     // close settings view
@@ -93,6 +93,16 @@ export class KeybindingSrv {
     if (search.editview) {
       delete search.editview;
       this.$location.search(search);
+      return;
+    }
+
+    if (search.fullscreen) {
+      this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
+      return;
+    }
+
+    if (search.kiosk) {
+      this.$rootScope.appEvent('toggle-kiosk-mode', { exit: true });
     }
   }
 

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

@@ -27,7 +27,7 @@ function getReactComponent(name, $injector) {
   }
 
   // ensure the specified React component is accessible, and fail fast if it's not
-  var reactComponent;
+  let reactComponent;
   try {
     reactComponent = $injector.get(name);
   } catch (e) {}
@@ -204,7 +204,7 @@ const reactComponent = function($injector) {
       attrs.props ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent) : renderMyComponent();
 
       // cleanup when scope is destroyed
-      scope.$on('$destroy', function() {
+      scope.$on('$destroy', () => {
         if (!attrs.onScopeDestroy) {
           ReactDOM.unmountComponentAtNode(elem[0]);
         } else {
@@ -256,7 +256,7 @@ const reactDirective = function($injector) {
 
         // for each of the properties, get their scope value and set it to scope.props
         const renderMyComponent = function() {
-          var scopeProps = {};
+          let scopeProps = {};
           const config = {};
 
           props.forEach(function(prop) {
@@ -280,7 +280,7 @@ const reactDirective = function($injector) {
         props.length ? watchProps(attrs.watchDepth, scope, propExpressions, renderMyComponent) : renderMyComponent();
 
         // cleanup when scope is destroyed
-        scope.$on('$destroy', function() {
+        scope.$on('$destroy', () => {
           if (!attrs.onScopeDestroy) {
             ReactDOM.unmountComponentAtNode(elem[0]);
           } else {

+ 2 - 2
public/app/core/services/popover_srv.ts

@@ -2,8 +2,8 @@ import _ from 'lodash';
 import coreModule from 'app/core/core_module';
 import Drop from 'tether-drop';
 
-/** @ngInject **/
-function popoverSrv($compile, $rootScope, $timeout) {
+/** @ngInject */
+function popoverSrv(this: any, $compile, $rootScope, $timeout) {
   let openDrop = null;
 
   this.close = function() {

+ 2 - 2
public/app/core/services/segment_srv.ts

@@ -2,10 +2,10 @@ import _ from 'lodash';
 import coreModule from '../core_module';
 
 /** @ngInject */
-export function uiSegmentSrv($sce, templateSrv) {
+export function uiSegmentSrv(this: any, $sce, templateSrv) {
   const self = this;
 
-  function MetricSegment(options) {
+  function MetricSegment(this: any, options) {
     if (options === '*' || options.value === '*') {
       this.value = '*';
       this.html = $sce.trustAsHtml('<i class="fa fa-asterisk"><i>');

+ 3 - 3
public/app/core/specs/datemath.test.ts

@@ -55,8 +55,8 @@ describe('DateMath', () => {
   });
 
   describe('subtraction', () => {
-    var now;
-    var anchored;
+    let now;
+    let anchored;
 
     beforeEach(() => {
       clock = sinon.useFakeTimers(unix);
@@ -83,7 +83,7 @@ describe('DateMath', () => {
   });
 
   describe('rounding', () => {
-    var now;
+    let now;
 
     beforeEach(() => {
       clock = sinon.useFakeTimers(unix);

+ 3 - 3
public/app/core/specs/table_model.test.ts

@@ -26,7 +26,7 @@ describe('when sorting table desc', () => {
 });
 
 describe('when sorting table asc', () => {
-  var table;
+  let table;
   const panel = {
     sort: { col: 1, desc: false },
   };
@@ -46,8 +46,8 @@ describe('when sorting table asc', () => {
 });
 
 describe('when sorting with nulls', () => {
-  var table;
-  var values;
+  let table;
+  let values;
 
   beforeEach(() => {
     table = new TableModel();

+ 3 - 3
public/app/core/specs/time_series.test.ts

@@ -174,7 +174,7 @@ describe('TimeSeries', function() {
   });
 
   describe('can detect if series contains ms precision', function() {
-    var fakedata;
+    let fakedata;
 
     beforeEach(function() {
       fakedata = testData;
@@ -194,7 +194,7 @@ describe('TimeSeries', function() {
   });
 
   describe('series overrides', function() {
-    var series;
+    let series;
     beforeEach(function() {
       series = new TimeSeries(testData);
     });
@@ -313,7 +313,7 @@ describe('TimeSeries', function() {
   });
 
   describe('value formatter', function() {
-    var series;
+    let series;
     beforeEach(function() {
       series = new TimeSeries(testData);
     });

+ 10 - 10
public/app/core/time_series2.ts

@@ -124,7 +124,7 @@ export default class TimeSeries {
     delete this.stack;
     delete this.bars.show;
 
-    for (var i = 0; i < overrides.length; i++) {
+    for (let i = 0; i < overrides.length; i++) {
       const override = overrides[i];
       if (!matchSeriesOverride(override.alias, this.alias)) {
         continue;
@@ -211,14 +211,14 @@ export default class TimeSeries {
 
     const ignoreNulls = fillStyle === 'connected';
     const nullAsZero = fillStyle === 'null as zero';
-    var currentTime;
-    var currentValue;
-    var nonNulls = 0;
-    var previousTime;
-    var previousValue = 0;
-    var previousDeltaUp = true;
-
-    for (var i = 0; i < this.datapoints.length; i++) {
+    let currentTime;
+    let currentValue;
+    let nonNulls = 0;
+    let previousTime;
+    let previousValue = 0;
+    let previousDeltaUp = true;
+
+    for (let i = 0; i < this.datapoints.length; i++) {
       currentValue = this.datapoints[i][0];
       currentTime = this.datapoints[i][1];
 
@@ -328,7 +328,7 @@ export default class TimeSeries {
   }
 
   isMsResolutionNeeded() {
-    for (var i = 0; i < this.datapoints.length; i++) {
+    for (let i = 0; i < this.datapoints.length; i++) {
       if (this.datapoints[i][1] !== null) {
         const timestamp = this.datapoints[i][1].toString();
         if (timestamp.length === 13 && timestamp % 1000 !== 0) {

+ 2 - 2
public/app/core/utils/css_loader.ts

@@ -11,7 +11,7 @@ for (let i = 0; i < links.length; i++) {
 const isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/);
 const webkitLoadCheck = function(link, callback) {
   setTimeout(function() {
-    for (var i = 0; i < document.styleSheets.length; i++) {
+    for (let i = 0; i < document.styleSheets.length; i++) {
       const sheet = document.styleSheets[i];
       if (sheet.href === link.href) {
         return callback();
@@ -68,7 +68,7 @@ export function fetch(load): any {
   }
 
   // don't reload styles loaded in the head
-  for (var i = 0; i < linkHrefs.length; i++) {
+  for (let i = 0; i < linkHrefs.length; i++) {
     if (load.address === linkHrefs[i]) {
       return '';
     }

+ 8 - 8
public/app/core/utils/datemath.ts

@@ -14,10 +14,10 @@ export function parse(text, roundUp?, timezone?) {
     return moment(text);
   }
 
-  var time;
-  var mathString = '';
-  var index;
-  var parseString;
+  let time;
+  let mathString = '';
+  let index;
+  let parseString;
 
   if (text.substring(0, 3) === 'now') {
     if (timezone === 'utc') {
@@ -61,14 +61,14 @@ export function isValid(text) {
 
 export function parseDateMath(mathString, time, roundUp?) {
   const dateTime = time;
-  var i = 0;
+  let i = 0;
   const len = mathString.length;
 
   while (i < len) {
     const c = mathString.charAt(i++);
-    var type;
-    var num;
-    var unit;
+    let type;
+    let num;
+    let unit;
 
     if (c === '/') {
       type = 0;

+ 6 - 6
public/app/core/utils/kbn.ts

@@ -169,8 +169,8 @@ kbn.intervals_in_seconds = {
 };
 
 kbn.calculateInterval = function(range, resolution, lowLimitInterval) {
-  var lowLimitMs = 1; // 1 millisecond default low limit
-  var intervalMs;
+  let lowLimitMs = 1; // 1 millisecond default low limit
+  let intervalMs;
 
   if (lowLimitInterval) {
     if (lowLimitInterval[0] === '>') {
@@ -304,7 +304,7 @@ kbn.formatBuilders.scaledUnits = function(factor, extArray) {
       return '';
     }
 
-    var steps = 0;
+    let steps = 0;
     const limit = extArray.length;
 
     while (Math.abs(size) >= factor) {
@@ -328,7 +328,7 @@ 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'];
+  let prefixes = ['n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
   prefixes = prefixes.slice(3 + (offset || 0));
   const units = prefixes.map(function(p) {
     return ' ' + p + unit;
@@ -790,8 +790,8 @@ kbn.toDuration = function(size, decimals, timeScale) {
 
   const strings = [];
   // after first value >= 1 print only $decimals more
-  var decrementDecimals = false;
-  for (var i = 0; i < units.length && decimals >= 0; i++) {
+  let decrementDecimals = false;
+  for (let i = 0; i < units.length && decimals >= 0; i++) {
     const interval = kbn.intervals_in_seconds[units[i].short] * 1000;
     const value = size / interval;
     if (value >= 1 || decrementDecimals) {

+ 1 - 1
public/app/core/utils/tags.ts

@@ -75,7 +75,7 @@ export function getTagColorsFromName(name: string): { color: string; borderColor
 
 function djb2(str) {
   let hash = 5381;
-  for (var i = 0; i < str.length; i++) {
+  for (let i = 0; i < str.length; i++) {
     hash = (hash << 5) + hash + str.charCodeAt(i); /* hash * 33 + c */
   }
   return hash;

+ 1 - 1
public/app/core/utils/url.ts

@@ -20,7 +20,7 @@ export function toUrlParams(a) {
   };
 
   const buildParams = function(prefix, obj) {
-    var i, len, key;
+    let i, len, key;
 
     if (prefix) {
       if (isArray(obj)) {

+ 2 - 2
public/app/features/admin/admin.ts

@@ -8,7 +8,7 @@ import coreModule from 'app/core/core_module';
 class AdminSettingsCtrl {
   navModel: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, backendSrv, navModelSrv) {
     this.navModel = navModelSrv.getNav('cfg', 'admin', 'server-settings', 1);
 
@@ -21,7 +21,7 @@ class AdminSettingsCtrl {
 class AdminHomeCtrl {
   navModel: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(navModelSrv) {
     this.navModel = navModelSrv.getNav('cfg', 'admin', 1);
   }

+ 1 - 1
public/app/features/admin/admin_list_users_ctrl.ts

@@ -26,7 +26,7 @@ export default class AdminListUsersCtrl {
         this.showPaging = this.totalPages > 1;
         this.pages = [];
 
-        for (var i = 1; i < this.totalPages + 1; i++) {
+        for (let i = 1; i < this.totalPages + 1; i++) {
           this.pages.push({ page: i, current: i === this.page });
         }
       });

+ 1 - 1
public/app/features/alerting/threshold_mapper.ts

@@ -1,6 +1,6 @@
 export class ThresholdMapper {
   static alertToGraphThresholds(panel) {
-    for (var i = 0; i < panel.alert.conditions.length; i++) {
+    for (let i = 0; i < panel.alert.conditions.length; i++) {
       const condition = panel.alert.conditions[i];
       if (condition.type !== 'query') {
         continue;

+ 6 - 6
public/app/features/annotations/annotation_tooltip.ts

@@ -3,7 +3,7 @@ import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 import alertDef from '../alerting/alert_def';
 
-/** @ngInject **/
+/** @ngInject */
 export function annotationTooltipDirective($sanitize, dashboardSrv, contextSrv, $compile) {
   function sanitizeString(str) {
     try {
@@ -22,12 +22,12 @@ export function annotationTooltipDirective($sanitize, dashboardSrv, contextSrv,
     },
     link: function(scope, element) {
       const event = scope.event;
-      var title = event.title;
-      var text = event.text;
+      let title = event.title;
+      let text = event.text;
       const dashboard = dashboardSrv.getCurrent();
 
-      var tooltip = '<div class="graph-annotation">';
-      var titleStateClass = '';
+      let tooltip = '<div class="graph-annotation">';
+      let titleStateClass = '';
 
       if (event.alertId) {
         const stateModel = alertDef.getStateDisplayModel(event.newState);
@@ -42,7 +42,7 @@ export function annotationTooltipDirective($sanitize, dashboardSrv, contextSrv,
         title = '';
       }
 
-      var header = `<div class="graph-annotation__header">`;
+      let header = `<div class="graph-annotation__header">`;
       if (event.login) {
         header += `<div class="graph-annotation__user" bs-tooltip="'Created by ${event.login}'"><img src="${
           event.avatarUrl

+ 1 - 1
public/app/features/annotations/annotations_srv.ts

@@ -25,7 +25,7 @@ export class AnnotationsSrv {
       .all([this.getGlobalAnnotations(options), this.getAlertStates(options)])
       .then(results => {
         // combine the annotations and flatten results
-        var annotations = _.flattenDeep(results[0]);
+        let annotations = _.flattenDeep(results[0]);
 
         // filter out annotations that do not belong to requesting panel
         annotations = _.filter(annotations, item => {

+ 1 - 1
public/app/features/annotations/event_editor.ts

@@ -12,7 +12,7 @@ export class EventEditorCtrl {
   close: any;
   timeFormated: string;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private annotationsSrv) {
     this.event.panelId = this.panelCtrl.panel.id;
     this.event.dashboardId = this.panelCtrl.dashboard.id;

+ 1 - 1
public/app/features/annotations/event_manager.ts

@@ -102,7 +102,7 @@ export class EventManager {
       }
     } else {
       // annotations from query
-      for (var i = 0; i < annotations.length; i++) {
+      for (let i = 0; i < annotations.length; i++) {
         const item = annotations[i];
 
         // add properties used by jquery flot events

+ 1 - 1
public/app/features/dashboard/create_folder_ctrl.ts

@@ -8,7 +8,7 @@ export class CreateFolderCtrl {
   hasValidationError: boolean;
   validationError: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private backendSrv, private $location, private validationSrv, navModelSrv) {
     this.navModel = navModelSrv.getNav('dashboards', 'manage-dashboards', 0);
   }

+ 1 - 1
public/app/features/dashboard/dashboard_ctrl.ts

@@ -144,7 +144,7 @@ export class DashboardCtrl implements PanelContainer {
   removePanel(panel: PanelModel, ask: boolean) {
     // confirm deletion
     if (ask !== false) {
-      var text2, confirmText;
+      let text2, confirmText;
 
       if (panel.alert) {
         text2 = 'Panel includes an alert rule, removing panel will also remove alert rule';

+ 1 - 1
public/app/features/dashboard/dashboard_loader_srv.ts

@@ -36,7 +36,7 @@ export class DashboardLoaderSrv {
   }
 
   loadDashboard(type, slug, uid) {
-    var promise;
+    let promise;
 
     if (type === 'script') {
       promise = this._loadScriptedDashboard(slug);

+ 8 - 11
public/app/features/dashboard/dashboard_migration.ts

@@ -18,7 +18,7 @@ export class DashboardMigrator {
   }
 
   updateSchema(old) {
-    var i, j, k, n;
+    let i, j, k, n;
     const oldVersion = this.dashboard.schemaVersion;
     const panelUpgrades = [];
     this.dashboard.schemaVersion = 16;
@@ -83,7 +83,7 @@ export class DashboardMigrator {
     // schema version 3 changes
     if (oldVersion < 3) {
       // ensure panel ids
-      var maxId = this.dashboard.getNextPanelId();
+      let maxId = this.dashboard.getNextPanelId();
       panelUpgrades.push(function(panel) {
         if (!panel.id) {
           panel.id = maxId;
@@ -140,15 +140,12 @@ export class DashboardMigrator {
       }
 
       // ensure query refIds
-      panelUpgrades.push(function(panel) {
-        _.each(
-          panel.targets,
-          function(target) {
-            if (!target.refId) {
-              target.refId = this.dashboard.getNextQueryLetter(panel);
-            }
-          }.bind(this)
-        );
+      panelUpgrades.push(panel => {
+        _.each(panel.targets, target => {
+          if (!target.refId) {
+            target.refId = this.dashboard.getNextQueryLetter(panel);
+          }
+        });
       });
     }
 

+ 14 - 6
public/app/features/dashboard/dashboard_model.ts

@@ -144,7 +144,7 @@ export class DashboardModel {
     });
 
     // make clone
-    var copy: any = {};
+    let copy: any = {};
     for (const property in this) {
       if (DashboardModel.nonPersistedProperties[property] || !this.hasOwnProperty(property)) {
         continue;
@@ -842,12 +842,20 @@ export class DashboardModel {
       })
     );
 
-    // Consider navbar and submenu controls, padding and margin
-    let visibleHeight = window.innerHeight - 55 - 20;
+    const navbarHeight = 55;
+    const margin = 20;
+    const submenuHeight = 50;
 
-    // Remove submenu if visible
-    if (this.meta.submenuEnabled) {
-      visibleHeight -= 50;
+    let visibleHeight = viewHeight - navbarHeight - margin;
+
+    // Remove submenu height if visible
+    if (this.meta.submenuEnabled && !this.meta.kiosk) {
+      visibleHeight -= submenuHeight;
+    }
+
+    // add back navbar height
+    if (this.meta.kiosk === 'b') {
+      visibleHeight += 55;
     }
 
     const visibleGridHeight = Math.floor(visibleHeight / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN));

+ 10 - 4
public/app/features/dashboard/dashnav/dashnav.html

@@ -8,14 +8,14 @@
 		</a>
 	</div>
 
+	<div class="navbar__spacer"></div>
+
 	<div class="navbar-buttons navbar-buttons--playlist" ng-if="ctrl.playlistSrv.isPlaying">
 		<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
 		<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
 		<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
 	</div>
 
-	<div class="navbar__spacer"></div>
-
 	<div class="navbar-buttons navbar-buttons--actions">
 		<button class="btn navbar-button navbar-button--add-panel" ng-show="::ctrl.dashboard.meta.canSave" bs-tooltip="'Add panel'" data-placement="bottom" ng-click="ctrl.addPanel()">
 			<i class="gicon gicon-add-panel"></i>
@@ -25,11 +25,11 @@
 			<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
 		</button>
 
-		<button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
+    <button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
 			<i class="fa fa-share-square-o"></i></a>
 		</button>
 
-		<button class="btn navbar-button navbar-button--save" ng-show="ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
+    <button class="btn navbar-button navbar-button--save" ng-show="ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
 			<i class="fa fa-save"></i>
 		</button>
 
@@ -42,6 +42,12 @@
 		</button>
 	</div>
 
+	<div class="navbar-buttons navbar-buttons--tv">
+    <button class="btn navbar-button navbar-button--tv" ng-click="ctrl.toggleViewMode()" bs-tooltip="'Cycle view mode'" data-placement="bottom">
+      <i class="fa fa-desktop"></i>
+    </button>
+  </div>
+
 	<gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
 
 	<div class="navbar-buttons navbar-buttons--close">

+ 4 - 0
public/app/features/dashboard/dashnav/dashnav.ts

@@ -31,6 +31,10 @@ export class DashNavCtrl {
     this.$location.search(search);
   }
 
+  toggleViewMode() {
+    appEvents.emit('toggle-kiosk-mode');
+  }
+
   close() {
     const search = this.$location.search();
     if (search.editview) {

+ 2 - 2
public/app/features/dashboard/export_data/export_data_modal.ts

@@ -5,9 +5,9 @@ import appEvents from 'app/core/app_events';
 export class ExportDataModalCtrl {
   private data: any[];
   private panel: string;
-  asRows: Boolean = true;
+  asRows = true;
   dateTimeFormat = 'YYYY-MM-DDTHH:mm:ssZ';
-  excel: false;
+  excel = false;
 
   export() {
     if (this.panel === 'table') {

+ 1 - 1
public/app/features/dashboard/repeat_option/repeat_option.ts

@@ -7,7 +7,7 @@ const template = `
 </div>
 `;
 
-/** @ngInject **/
+/** @ngInject */
 function dashRepeatOptionDirective(variableSrv) {
   return {
     restrict: 'E',

+ 2 - 2
public/app/features/dashboard/settings/settings.ts

@@ -179,8 +179,8 @@ export class SettingsCtrl {
   }
 
   deleteDashboard() {
-    var confirmText = '';
-    var text2 = this.dashboard.title;
+    let confirmText = '';
+    let text2 = this.dashboard.title;
 
     const alerts = _.sumBy(this.dashboard.panels, panel => {
       return panel.alert ? 1 : 0;

+ 2 - 2
public/app/features/dashboard/shareModalCtrl.ts

@@ -35,7 +35,7 @@ export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv,
   };
 
   $scope.buildUrl = function() {
-    var baseUrl = $location.absUrl();
+    let baseUrl = $location.absUrl();
     const queryStart = baseUrl.indexOf('?');
 
     if (queryStart !== -1) {
@@ -72,7 +72,7 @@ export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv,
 
     $scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params);
 
-    var soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/');
+    let soloUrl = baseUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/');
     soloUrl = soloUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/');
     delete params.fullscreen;
     delete params.edit;

+ 1 - 1
public/app/features/dashboard/share_snapshot_ctrl.ts

@@ -2,7 +2,7 @@ import angular from 'angular';
 import _ from 'lodash';
 
 export class ShareSnapshotCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, $rootScope, $location, backendSrv, $timeout, timeSrv) {
     $scope.snapshot = {
       name: $scope.dashboard.title,

+ 3 - 3
public/app/features/dashboard/specs/repeat.test.ts

@@ -5,7 +5,7 @@ import { expect } from 'test/lib/common';
 jest.mock('app/core/services/context_srv', () => ({}));
 
 describe('given dashboard with panel repeat', function() {
-  var dashboard;
+  let dashboard;
 
   beforeEach(function() {
     const dashboardJSON = {
@@ -56,7 +56,7 @@ describe('given dashboard with panel repeat', function() {
 });
 
 describe('given dashboard with panel repeat in horizontal direction', function() {
-  var dashboard;
+  let dashboard;
 
   beforeEach(function() {
     dashboard = new DashboardModel({
@@ -188,7 +188,7 @@ describe('given dashboard with panel repeat in horizontal direction', function()
 });
 
 describe('given dashboard with panel repeat in vertical direction', function() {
-  var dashboard;
+  let dashboard;
 
   beforeEach(function() {
     dashboard = new DashboardModel({

+ 1 - 1
public/app/features/dashboard/time_srv.ts

@@ -13,7 +13,7 @@ export class TimeSrv {
   timeAtLoad: any;
   private autoRefreshBlocked: boolean;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private $rootScope, private $timeout, private $location, private timer, private contextSrv) {
     // default time
     this.time = { from: '6h', to: 'now' };

+ 1 - 1
public/app/features/dashboard/timepicker/input_date.ts

@@ -18,7 +18,7 @@ export function inputDateDirective() {
           return text;
         }
 
-        var parsed;
+        let parsed;
         if ($scope.ctrl.isUtc) {
           parsed = moment.utc(text, format);
         } else {

+ 11 - 13
public/app/features/dashboard/timepicker/timepicker.html

@@ -1,18 +1,8 @@
-<div class="navbar-buttons navbar-buttons--zoom">
-	<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(-1)'>
+<div class="navbar-buttons">
+  <button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(-1)' ng-if="ctrl.isAbsolute">
 		<i class="fa fa-chevron-left"></i>
 	</button>
 
-	<button class="btn navbar-button" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom" ng-click='ctrl.zoom(2)'>
-		<i class="fa fa-search-minus"></i>
-	</button>
-
-	<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(1)'>
-		<i class="fa fa-chevron-right"></i>
-	</button>
-</div>
-
-<div class="navbar-buttons">
 	<button bs-tooltip="ctrl.tooltip" data-placement="bottom" ng-click="ctrl.openDropdown()" class="btn navbar-button gf-timepicker-nav-btn">
 		<i class="fa fa-clock-o"></i>
 		<span ng-bind="ctrl.rangeString"></span>
@@ -20,7 +10,15 @@
 		<span ng-show="ctrl.dashboard.refresh" class="text-warning">&nbsp; Refresh every {{ctrl.dashboard.refresh}}</span>
 	</button>
 
-	<button class="btn navbar-button navbar-button--refresh" ng-click="ctrl.timeSrv.refreshDashboard()">
+  <button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(1)' ng-if="ctrl.isAbsolute">
+		<i class="fa fa-chevron-right"></i>
+	</button>
+
+  <button class="btn navbar-button navbar-button--zoom" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom" ng-click='ctrl.zoom(2)'>
+		<i class="fa fa-search-minus"></i>
+	</button>
+
+  <button class="btn navbar-button navbar-button--refresh" ng-click="ctrl.timeSrv.refreshDashboard()">
 		<i class="fa fa-refresh"></i>
 	</button>
 </div>

+ 3 - 1
public/app/features/dashboard/timepicker/timepicker.ts

@@ -23,6 +23,7 @@ export class TimePickerCtrl {
   isUtc: boolean;
   firstDayOfWeek: number;
   isOpen: boolean;
+  isAbsolute: boolean;
 
   /** @ngInject */
   constructor(private $scope, private $rootScope, private timeSrv) {
@@ -65,6 +66,7 @@ export class TimePickerCtrl {
     this.tooltip = this.dashboard.formatDate(time.from) + ' <br>to<br>';
     this.tooltip += this.dashboard.formatDate(time.to);
     this.timeRaw = timeRaw;
+    this.isAbsolute = moment.isMoment(this.timeRaw.to);
   }
 
   zoom(factor) {
@@ -75,7 +77,7 @@ export class TimePickerCtrl {
     const range = this.timeSrv.timeRange();
 
     const timespan = (range.to.valueOf() - range.from.valueOf()) / 2;
-    var to, from;
+    let to, from;
     if (direction === -1) {
       to = range.to.valueOf() - timespan;
       from = range.from.valueOf() - timespan;

+ 1 - 1
public/app/features/dashboard/unsaved_changes_srv.ts

@@ -2,7 +2,7 @@ import angular from 'angular';
 import { ChangeTracker } from './change_tracker';
 
 /** @ngInject */
-export function unsavedChangesSrv($rootScope, $q, $location, $timeout, contextSrv, dashboardSrv, $window) {
+export function unsavedChangesSrv(this: any, $rootScope, $q, $location, $timeout, contextSrv, dashboardSrv, $window) {
   this.init = function(dashboard, scope) {
     this.tracker = new ChangeTracker(dashboard, scope, 1000, $location, $window, $timeout, contextSrv, $rootScope);
     return this.tracker;

+ 2 - 2
public/app/features/dashboard/upload.ts

@@ -21,7 +21,7 @@ function uploadDashboardDirective(timer, alertSrv, $location) {
         const files = evt.target.files; // FileList object
         const readerOnload = function() {
           return function(e) {
-            var dash;
+            let dash;
             try {
               dash = JSON.parse(e.target.result);
             } catch (err) {
@@ -36,7 +36,7 @@ function uploadDashboardDirective(timer, alertSrv, $location) {
           };
         };
 
-        for (var i = 0, f; (f = files[i]); i++) {
+        for (let i = 0, f; (f = files[i]); i++) {
           const reader = new FileReader();
           reader.onload = readerOnload();
           reader.readAsText(f);

+ 1 - 1
public/app/features/dashlinks/editor.ts

@@ -1,7 +1,7 @@
 import angular from 'angular';
 import _ from 'lodash';
 
-export var iconMap = {
+export let iconMap = {
   'external link': 'fa-external-link',
   dashboard: 'fa-th-large',
   question: 'fa-question',

+ 1 - 1
public/app/features/dashlinks/module.ts

@@ -20,7 +20,7 @@ function dashLink($compile, $sanitize, linkSrv) {
     restrict: 'E',
     link: function(scope, elem) {
       const link = scope.link;
-      var template =
+      let template =
         '<div class="gf-form">' +
         '<a class="pointer gf-form-label" data-placement="bottom"' +
         (link.asDropdown ? ' ng-click="fillDropdown(link)" data-toggle="dropdown"' : '') +

+ 1 - 1
public/app/features/org/change_password_ctrl.ts

@@ -2,7 +2,7 @@ import angular from 'angular';
 import config from 'app/core/config';
 
 export class ChangePasswordCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, backendSrv, $location, navModelSrv) {
     $scope.command = {};
     $scope.authProxyEnabled = config.authProxyEnabled;

+ 1 - 1
public/app/features/org/create_team_ctrl.ts

@@ -5,7 +5,7 @@ export default class CreateTeamCtrl {
   email: string;
   navModel: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private backendSrv, private $location, navModelSrv) {
     this.navModel = navModelSrv.getNav('cfg', 'teams', 0);
   }

+ 1 - 1
public/app/features/org/new_org_ctrl.ts

@@ -2,7 +2,7 @@ import angular from 'angular';
 import config from 'app/core/config';
 
 export class NewOrgCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, $http, backendSrv, navModelSrv) {
     $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs', 1);
     $scope.newOrg = { name: '' };

+ 1 - 1
public/app/features/org/org_api_keys_ctrl.ts

@@ -1,7 +1,7 @@
 import angular from 'angular';
 
 export class OrgApiKeysCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, $http, backendSrv, navModelSrv) {
     $scope.navModel = navModelSrv.getNav('cfg', 'apikeys', 0);
 

+ 1 - 1
public/app/features/org/org_details_ctrl.ts

@@ -1,7 +1,7 @@
 import angular from 'angular';
 
 export class OrgDetailsCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, $http, backendSrv, contextSrv, navModelSrv) {
     $scope.init = function() {
       $scope.getOrgInfo();

+ 1 - 1
public/app/features/org/prefs_control.ts

@@ -14,7 +14,7 @@ export class PrefsControlCtrl {
   ];
   themes: any = [{ value: '', text: 'Default' }, { value: 'dark', text: 'Dark' }, { value: 'light', text: 'Light' }];
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private backendSrv, private $location) {}
 
   $onInit() {

+ 1 - 1
public/app/features/org/profile_ctrl.ts

@@ -12,7 +12,7 @@ export class ProfileCtrl {
   readonlyLoginFields = config.disableLoginForm;
   navModel: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
     this.getUser();
     this.getUserTeams();

+ 1 - 1
public/app/features/org/select_org_ctrl.ts

@@ -2,7 +2,7 @@ import angular from 'angular';
 import config from 'app/core/config';
 
 export class SelectOrgCtrl {
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, backendSrv, contextSrv) {
     contextSrv.sidemenu = false;
 

+ 1 - 1
public/app/features/org/user_invite_ctrl.ts

@@ -5,7 +5,7 @@ export class UserInviteCtrl {
   invite: any;
   inviteForm: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private backendSrv, navModelSrv, private $location) {
     this.navModel = navModelSrv.getNav('cfg', 'users', 0);
 

+ 2 - 2
public/app/features/panel/metrics_panel_ctrl.ts

@@ -75,7 +75,7 @@ class MetricsPanelCtrl extends PanelCtrl {
     // if we have snapshot data use that
     if (this.panel.snapshotData) {
       this.updateTimeRange();
-      var data = this.panel.snapshotData;
+      let data = this.panel.snapshotData;
       // backward compatibility
       if (!_.isArray(data)) {
         data = data.data;
@@ -155,7 +155,7 @@ class MetricsPanelCtrl extends PanelCtrl {
   }
 
   calculateInterval() {
-    var intervalOverride = this.panel.interval;
+    let intervalOverride = this.panel.interval;
 
     // if no panel interval check datasource
     if (intervalOverride) {

+ 1 - 1
public/app/features/panel/metrics_tab.ts

@@ -110,7 +110,7 @@ export class MetricsTabCtrl {
   }
 }
 
-/** @ngInject **/
+/** @ngInject */
 export function metricsTabDirective() {
   'use strict';
   return {

+ 2 - 2
public/app/features/panel/panel_ctrl.ts

@@ -317,7 +317,7 @@ export class PanelCtrl {
   }
 
   getInfoContent(options) {
-    var markdown = this.panel.description;
+    let markdown = this.panel.description;
 
     if (options.mode === 'tooltip') {
       markdown = this.error || this.panel.description;
@@ -327,7 +327,7 @@ export class PanelCtrl {
     const sanitize = this.$injector.get('$sanitize');
     const templateSrv = this.$injector.get('templateSrv');
     const interpolatedMarkdown = templateSrv.replace(markdown, this.panel.scopedVars);
-    var html = '<div class="markdown-html">';
+    let html = '<div class="markdown-html">';
 
     html += new Remarkable().render(interpolatedMarkdown);
 

+ 1 - 1
public/app/features/panel/panel_header.ts

@@ -80,7 +80,7 @@ function createMenuTemplate(ctrl) {
   return html;
 }
 
-/** @ngInject **/
+/** @ngInject */
 function panelHeader($compile) {
   return {
     restrict: 'E',

+ 1 - 1
public/app/features/panel/query_editor_row.ts

@@ -87,7 +87,7 @@ export class QueryRowCtrl {
   }
 }
 
-/** @ngInject **/
+/** @ngInject */
 function queryEditorRowDirective() {
   return {
     restrict: 'E',

+ 2 - 2
public/app/features/panel/query_troubleshooter.ts

@@ -40,7 +40,7 @@ export class QueryTroubleshooterCtrl {
   mockedResponse: string;
   jsonExplorer: JsonExplorer;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor($scope, private $timeout) {
     this.onRequestErrorEventListener = this.onRequestError.bind(this);
     this.onRequestResponseEventListener = this.onRequestResponse.bind(this);
@@ -87,7 +87,7 @@ export class QueryTroubleshooterCtrl {
   }
 
   handleMocking(data) {
-    var mockedData;
+    let mockedData;
     try {
       mockedData = JSON.parse(this.mockedResponse);
     } catch (err) {

+ 62 - 74
public/app/features/playlist/partials/playlist.html

@@ -1,9 +1,9 @@
 <page-header model="ctrl.navModel"></page-header>
 
-<div class="page-container page-body" ng-form="playlistEditForm">
+<div class="page-container page-body" ng-form="ctrl.playlistEditForm">
 
-  <h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
-  <h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
+	<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
+	<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
 
 	<p class="playlist-description">A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.</p>
 
@@ -20,79 +20,71 @@
 
 	<div class="gf-form-group">
 		<h3 class="page-headering">Dashboards</h3>
-	</div>
 
-	<div class="row">
-		<div class="col-lg-6">
-			<div class="playlist-search-containerwrapper">
-				<div class="max-width-32">
-					<h5 class="page-headering playlist-column-header">Available</h5>
-					<div style="">
-						<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
-					</div>
-				</div>
-			</div>
+		<table class="filter-table playlist-available-list">
+			<tr ng-repeat="playlistItem in ctrl.playlistItems">
+				<td ng-if="playlistItem.type === 'dashboard_by_id'">
+					<i class="icon-gf icon-gf-dashboard"></i>&nbsp;&nbsp;{{playlistItem.title}}
+				</td>
+				<td ng-if="playlistItem.type === 'dashboard_by_tag'">
+					<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
+						<i class="fa fa-tag"></i>
+						<span>{{playlistItem.title}}</span>
+					</a>
+				</td>
 
-			<div ng-if="ctrl.filteredDashboards.length > 0">
-				<table class="filter-table playlist-available-list">
-					<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
-						<td>
-							<i class="icon-gf icon-gf-dashboard"></i>
-							&nbsp;&nbsp;{{playlistItem.title}}
-							<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
-						</td>
-						<td class="add-dashboard">
-							<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
-								<i class="fa fa-plus"></i>
-								Add to playlist
-							</button>
-						</td>
-					</tr>
-				</table>
-			</div>
-			<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
-				<table class="filter-table playlist-available-list">
-					<tr ng-repeat="tag in ctrl.filteredTags">
-						<td>
-							<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
-								<i class="fa fa-tag"></i>
-								<span>{{tag.term}} &nbsp;({{tag.count}})</span>
-							</a>
-						</td>
-						<td class="add-dashboard">
-							<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
-								<i class="fa fa-plus"></i>
-								Add to playlist
-							</button>
-						</td>
-					</tr>
-				</table>
-			</div>
-		</div>
+				<td class="selected-playlistitem-settings">
+					<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
+						<i class="fa fa-arrow-up"></i>
+					</button>
+					<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
+						<i class="fa fa-arrow-down"></i>
+					</button>
+					<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
+						<i class="fa fa-remove"></i>
+					</button>
+				</td>
+			</tr>
+			<tr ng-if="ctrl.playlistItems.length === 0">
+				<td><em>Playlist is empty, add dashboards below.</em></td>
+			</tr>
+		</table>
+	</div>
 
-		<div class="col-lg-6">
-			<h5 class="page headering playlist-column-header">Selected</h5>
+	<div class="gf-form-group">
+		<h3 class="page-headering">Add dashboards</h3>
+		<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
+
+		<div ng-if="ctrl.filteredDashboards.length > 0">
 			<table class="filter-table playlist-available-list">
-				<tr ng-repeat="playlistItem in ctrl.playlistItems">
-					<td ng-if="playlistItem.type === 'dashboard_by_id'">
-						<i class="icon-gf icon-gf-dashboard"></i>&nbsp;&nbsp;{{playlistItem.title}}
+				<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
+					<td>
+						<i class="icon-gf icon-gf-dashboard"></i>
+						&nbsp;&nbsp;{{playlistItem.title}}
+						<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
+					</td>
+					<td class="add-dashboard">
+						<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
+							<i class="fa fa-plus"></i>
+							Add to playlist
+						</button>
 					</td>
-					<td ng-if="playlistItem.type === 'dashboard_by_tag'">
-						<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
+				</tr>
+			</table>
+		</div>
+		<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
+			<table class="filter-table playlist-available-list">
+				<tr ng-repeat="tag in ctrl.filteredTags">
+					<td>
+						<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
 							<i class="fa fa-tag"></i>
-							<span>{{playlistItem.title}}</span>
+							<span>{{tag.term}} &nbsp;({{tag.count}})</span>
 						</a>
 					</td>
-
-					<td class="selected-playlistitem-settings">
-						<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
-							<i class="fa fa-arrow-up"></i>
-						</button>
-						<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
-							<i class="fa fa-arrow-down"></i>
-						</button>
-						<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
-							<i class="fa fa-remove"></i>
+					<td class="add-dashboard">
+						<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
+							<i class="fa fa-plus"></i>
+							Add to playlist
 						</button>
 					</td>
 				</tr>
@@ -103,12 +95,8 @@
 	<div class="clearfix"></div>
 
 	<div class="gf-form-button-row">
-		<a class="btn btn-success " ng-show="ctrl.isNew"
-			ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
-			ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create new playlist</a>
-		<a class="btn btn-success" ng-show="!ctrl.isNew()"
-			ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
-			ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
+		<a class="btn btn-success" ng-show="ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create</a>
+		<a class="btn btn-success" ng-show="!ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
 		<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
 	</div>
 </div>

+ 30 - 28
public/app/features/playlist/partials/playlists.html

@@ -10,38 +10,42 @@
       </a>
     </div>
 
-    <table class="filter-table">
+    <table class="filter-table filter-table--hover">
       <thead>
-        <th>
-          <strong>Name</strong>
-        </th>
-        <th>
-          <strong>Start url</strong>
-        </th>
+        <th><strong>Name</strong></th>
+        <th style="width: 100px"></th>
         <th style="width: 78px"></th>
-        <th style="width: 78px"></th>
-        <th style="width: 25px"></th>
       </thead>
       <tr ng-repeat="playlist in ctrl.playlists">
-        <td>
+        <td class="link-td">
           <a href="playlists/edit/{{playlist.id}}">{{playlist.name}}</a>
         </td>
-        <td>
-          <a href="playlists/play/{{playlist.id}}">playlists/play/{{playlist.id}}</a>
-        </td>
-        <td class="text-center">
-          <a href="playlists/play/{{playlist.id}}" class="btn btn-inverse btn-small">
-            <i class="fa fa-play"></i>
-            Play
-          </a>
+        <td class="dropdown">
+          <button class="btn btn-inverse btn-small" data-toggle="dropdown">
+            Start playlist
+            <i class="fa fa-caret-down"></i>
+          </button>
+          <ul class="dropdown-menu" role="menu">
+            <li>
+              <a href="{{playlist.startUrl}}">
+                <i class="fa fa-play"></i> In Normal mode</span>
+              </a>
+              <a href="{{playlist.startUrl}}?kiosk=tv">
+                <i class="fa fa-play"></i> In TV mode</span>
+              </a>
+              <a href="{{playlist.startUrl}}?kiosk=tv&autofitpanels">
+                <i class="fa fa-play"></i> In TV mode <span class="muted">(with auto fit panels)</span>
+              </a>
+              <a href="{{playlist.startUrl}}?kiosk">
+                <i class="fa fa-play"></i> In Kiosk mode</span>
+              </a>
+              <a ng-href="{{playlist.startUrl}}?kiosk&autofitpanels">
+                <i class="fa fa-play"></i> In Kiosk mode <span class="muted">(with auto fit panels)</span>
+              </a>
+            </li>
+          </ul>
         </td>
-        <td class="text-right">
-          <a href="playlists/edit/{{playlist.id}}" class="btn btn-inverse btn-small">
-            <i class="fa fa-edit"></i>
-            Edit
-          </a>
-        </td>
-        <td class="text-right">
+        <td  class="text-right">
           <a ng-click="ctrl.removePlaylist(playlist)" class="btn btn-danger btn-small">
             <i class="fa fa-remove"></i>
           </a>
@@ -49,18 +53,16 @@
       </tr>
     </table>
   </div>
-
   <div ng-if="ctrl.playlists.length === 0">
     <empty-list-cta model="{
       title: 'There are no playlists created yet',
       buttonIcon: 'fa fa-plus',
       buttonLink: 'playlists/create',
       buttonTitle: ' Create Playlist',
-      proTip: 'You can run the playlist in Kiosk Mode.',
+      proTip: 'You can use playlists to remove control TVs',
       proTipLink: 'http://docs.grafana.org/reference/playlist/',
       proTipLinkTitle: 'Learn more',
       proTipTarget: '_blank'
     }" />
   </div>
-
 </div>

+ 2 - 13
public/app/features/playlist/playlist_edit_ctrl.ts

@@ -19,29 +19,18 @@ export class PlaylistEditCtrl {
   /** @ngInject */
   constructor(private $scope, private backendSrv, private $location, $route, navModelSrv) {
     this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
-    this.isNew = $route.current.params.id;
+    this.isNew = !$route.current.params.id;
 
     if ($route.current.params.id) {
       const playlistId = $route.current.params.id;
 
       backendSrv.get('/api/playlists/' + playlistId).then(result => {
         this.playlist = result;
-        this.navModel.node = {
-          text: result.name,
-          icon: this.navModel.node.icon,
-        };
-        this.navModel.breadcrumbs.push(this.navModel.node);
       });
 
       backendSrv.get('/api/playlists/' + playlistId + '/items').then(result => {
         this.playlistItems = result;
       });
-    } else {
-      this.navModel.node = {
-        text: 'New playlist',
-        icon: this.navModel.node.icon,
-      };
-      this.navModel.breadcrumbs.push(this.navModel.node);
     }
   }
 
@@ -88,7 +77,7 @@ export class PlaylistEditCtrl {
   }
 
   savePlaylist(playlist, playlistItems) {
-    var savePromise;
+    let savePromise;
 
     playlist.items = playlistItems;
 

+ 1 - 3
public/app/features/playlist/playlist_routes.ts

@@ -19,9 +19,7 @@ function grafanaRoutes($routeProvider) {
       controller: 'PlaylistEditCtrl',
     })
     .when('/playlists/play/:id', {
-      templateUrl: 'public/app/features/playlist/partials/playlists.html',
-      controllerAs: 'ctrl',
-      controller: 'PlaylistsCtrl',
+      template: '',
       resolve: {
         init: function(playlistSrv, $route) {
           const playlistId = $route.current.params.id;

+ 1 - 1
public/app/features/playlist/playlist_search.ts

@@ -8,7 +8,7 @@ export class PlaylistSearchCtrl {
 
   /** @ngInject */
   constructor($timeout, private backendSrv) {
-    this.query = { query: '', tag: [], starred: false, limit: 30 };
+    this.query = { query: '', tag: [], starred: false, limit: 20 };
 
     $timeout(() => {
       this.query.query = '';

+ 16 - 26
public/app/features/playlist/playlist_srv.ts

@@ -1,6 +1,8 @@
 import coreModule from '../../core/core_module';
 import kbn from 'app/core/utils/kbn';
 import appEvents from 'app/core/app_events';
+import _ from 'lodash';
+import { toUrlParams } from 'app/core/utils/url';
 
 class PlaylistSrv {
   private cancelPromise: any;
@@ -8,45 +10,30 @@ class PlaylistSrv {
   private index: number;
   private interval: any;
   private startUrl: string;
-  public isPlaying: boolean;
+  isPlaying: boolean;
 
   /** @ngInject */
-  constructor(private $location: any, private $timeout: any, private backendSrv: any, private $routeParams: any) {}
+  constructor(private $location: any, private $timeout: any, private backendSrv: any) {}
 
   next() {
     this.$timeout.cancel(this.cancelPromise);
 
     const playedAllDashboards = this.index > this.dashboards.length - 1;
-
     if (playedAllDashboards) {
-      window.location.href = this.getUrlWithKioskMode();
+      window.location.href = this.startUrl;
       return;
     }
 
     const dash = this.dashboards[this.index];
-    this.$location.url('dashboard/' + dash.uri);
+    const queryParams = this.$location.search();
+    const filteredParams = _.pickBy(queryParams, value => value !== null);
+
+    this.$location.url('dashboard/' + dash.uri + '?' + toUrlParams(filteredParams));
 
     this.index++;
     this.cancelPromise = this.$timeout(() => this.next(), this.interval);
   }
 
-  getUrlWithKioskMode() {
-    const inKioskMode = document.body.classList.contains('page-kiosk-mode');
-
-    // check if should add kiosk query param
-    if (inKioskMode && this.startUrl.indexOf('kiosk') === -1) {
-      return this.startUrl + '?kiosk=true';
-    }
-
-    // check if should remove kiosk query param
-    if (!inKioskMode) {
-      return this.startUrl.split('?')[0];
-    }
-
-    // already has kiosk query param, just return startUrl
-    return this.startUrl;
-  }
-
   prev() {
     this.index = Math.max(this.index - 2, 0);
     this.next();
@@ -59,10 +46,6 @@ class PlaylistSrv {
     this.index = 0;
     this.isPlaying = true;
 
-    if (this.$routeParams.kiosk) {
-      appEvents.emit('toggle-kiosk-mode');
-    }
-
     this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => {
       this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then(dashboards => {
         this.dashboards = dashboards;
@@ -73,6 +56,13 @@ class PlaylistSrv {
   }
 
   stop() {
+    if (this.isPlaying) {
+      const queryParams = this.$location.search();
+      if (queryParams.kiosk) {
+        appEvents.emit('toggle-kiosk-mode', { exit: true });
+      }
+    }
+
     this.index = 0;
     this.isPlaying = false;
 

+ 4 - 1
public/app/features/playlist/playlists_ctrl.ts

@@ -10,7 +10,10 @@ export class PlaylistsCtrl {
     this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
 
     backendSrv.get('/api/playlists').then(result => {
-      this.playlists = result;
+      this.playlists = result.map(item => {
+        item.startUrl = `playlists/play/${item.id}`;
+        return item;
+      });
     });
   }
 

+ 1 - 1
public/app/features/playlist/specs/playlist_edit_ctrl.test.ts

@@ -2,7 +2,7 @@ import '../playlist_edit_ctrl';
 import { PlaylistEditCtrl } from '../playlist_edit_ctrl';
 
 describe('PlaylistEditCtrl', () => {
-  var ctx: any;
+  let ctx: any;
   beforeEach(() => {
     const navModelSrv = {
       getNav: () => {

+ 2 - 2
public/app/features/plugins/datasource_srv.ts

@@ -136,13 +136,13 @@ export class DatasourceSrv {
 
   addDataSourceVariables(list) {
     // look for data source variables
-    for (var i = 0; i < this.templateSrv.variables.length; i++) {
+    for (let i = 0; i < this.templateSrv.variables.length; i++) {
       const variable = this.templateSrv.variables[i];
       if (variable.type !== 'datasource') {
         continue;
       }
 
-      var first = variable.current.value;
+      let first = variable.current.value;
       if (first === 'default') {
         first = config.defaultDatasource;
       }

+ 2 - 2
public/app/features/plugins/ds_edit_ctrl.ts

@@ -4,7 +4,7 @@ import config from 'app/core/config';
 import { coreModule, appEvents } from 'app/core/core';
 import { store } from 'app/stores/store';
 
-var datasourceTypes = [];
+let datasourceTypes = [];
 
 const defaults = {
   name: '',
@@ -16,7 +16,7 @@ const defaults = {
   secureJsonData: {},
 };
 
-var datasourceCreated = false;
+let datasourceCreated = false;
 
 export class DataSourceEditCtrl {
   isNew: boolean;

+ 2 - 2
public/app/features/plugins/plugin_component.ts

@@ -7,7 +7,7 @@ import { importPluginModule } from './plugin_loader';
 
 import { UnknownPanelCtrl } from 'app/plugins/panel/unknown/module';
 
-/** @ngInject **/
+/** @ngInject */
 function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
   function getTemplate(component) {
     if (component.template) {
@@ -69,7 +69,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
     };
 
     const panelInfo = config.panels[scope.panel.type];
-    var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
+    let panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
     if (panelInfo) {
       panelCtrlPromise = importPluginModule(panelInfo.module).then(function(panelModule) {
         return panelModule.PanelCtrl;

+ 1 - 1
public/app/features/styleguide/styleguide.ts

@@ -8,7 +8,7 @@ class StyleGuideCtrl {
   buttonVariants = ['-'];
   navModel: any;
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private $routeParams, private backendSrv, navModelSrv) {
     this.navModel = navModelSrv.getNav('cfg', 'admin', 'styleguide', 1);
     this.theme = config.bootData.user.lightTheme ? 'light' : 'dark';

+ 1 - 1
public/app/features/templating/adhoc_variable.ts

@@ -15,7 +15,7 @@ export class AdhocVariable implements Variable {
     skipUrlSync: false,
   };
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private model) {
     assignModelProperties(this, model, this.defaults);
   }

+ 1 - 1
public/app/features/templating/constant_variable.ts

@@ -17,7 +17,7 @@ export class ConstantVariable implements Variable {
     skipUrlSync: false,
   };
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private model, private variableSrv) {
     assignModelProperties(this, model, this.defaults);
   }

+ 1 - 1
public/app/features/templating/custom_variable.ts

@@ -23,7 +23,7 @@ export class CustomVariable implements Variable {
     skipUrlSync: false,
   };
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private model, private variableSrv) {
     assignModelProperties(this, model, this.defaults);
   }

+ 3 - 3
public/app/features/templating/datasource_variable.ts

@@ -22,7 +22,7 @@ export class DatasourceVariable implements Variable {
     skipUrlSync: false,
   };
 
-  /** @ngInject **/
+  /** @ngInject */
   constructor(private model, private datasourceSrv, private variableSrv, private templateSrv) {
     assignModelProperties(this, model, this.defaults);
     this.refresh = 1;
@@ -43,14 +43,14 @@ export class DatasourceVariable implements Variable {
   updateOptions() {
     const options = [];
     const sources = this.datasourceSrv.getMetricSources({ skipVariables: true });
-    var regex;
+    let regex;
 
     if (this.regex) {
       regex = this.templateSrv.replace(this.regex, null, 'regex');
       regex = kbn.stringToJsRegex(regex);
     }
 
-    for (var i = 0; i < sources.length; i++) {
+    for (let i = 0; i < sources.length; i++) {
       const source = sources[i];
       // must match on type
       if (source.meta.id !== this.query) {

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác