diff --git a/.jsfmtrc b/.jsfmtrc new file mode 100644 index 00000000000..3ca3b4a3b50 --- /dev/null +++ b/.jsfmtrc @@ -0,0 +1,21 @@ +{ + "preset" : "default", + + "lineBreak" : { + "before" : { + "VariableDeclarationWithoutInit" : 0, + }, + + "after": { + "AssignmentOperator": -1, + "ArgumentListArrayExpression": ">=1" + } + }, + + "whiteSpace" : { + "before" : { + }, + "after" : { + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 69835953dc1..c0f1625ac12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,24 @@ -# 1.8.0 (unreleased) +# 1.9.0 (unreleased) + +**UI Improvements* +- [Issue #770](https://github.com/grafana/grafana/issues/770). UI: Panel dropdown menu replaced with a new panel menu + +- [Issue #877](https://github.com/grafana/grafana/issues/877). Graph: Smart auto decimal precision when using scaled unit formats +- [Issue #850](https://github.com/grafana/grafana/issues/850). Graph: Shared tooltip that shows multiple series & crosshair line, thx @toni-moreno + +# 1.8.1 (unreleased) + +**Fixes** +- [Issue #847](https://github.com/grafana/grafana/issues/847). Graph: Fix for series draw order not being the same after hiding/unhiding series +- [Issue #851](https://github.com/grafana/grafana/issues/851). Annotations: Fix for annotations not reloaded when switching between 2 dashboards with annotations +- [Issue #846](https://github.com/grafana/grafana/issues/846). Edit panes: Issue when open row or json editor when scrolled down the page, unable to scroll and you did not see editor +- [Issue #840](https://github.com/grafana/grafana/issues/840). Import: Fixes to import from json file and import from graphite. Issues was lingering state from previous dashboard. +- [Issue #859](https://github.com/grafana/grafana/issues/859). InfluxDB: Fix for bug when saving dashboard where title is the same as slugified url id +- [Issue #852](https://github.com/grafana/grafana/issues/852). White theme: Fixes for hidden series legend text and disabled annotations color + +# 1.8.0 (2014-09-22) + +Read this [blog post](http://grafana.org/blog/2014/09/11/grafana-1-8-0-rc1-released.html) for an overview of all improvements. **Fixes** - [Issue #802](https://github.com/grafana/grafana/issues/802). Annotations: Fix when using InfluxDB datasource diff --git a/latest.json b/latest.json index c66de1bc179..30dd2d3127b 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "version": "1.8.0-rc1", - "url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.0-rc1" + "version": "1.8.1", + "url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.1.tar.gz" } diff --git a/package.json b/package.json index f84ca9cf8c6..8369d640c62 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "1.8.0-rc1", + "version": "1.8.1", "repository": { "type": "git", "url": "http://github.com/torkelo/grafana.git" diff --git a/src/app/components/kbn.js b/src/app/components/kbn.js index 7c61b22c83b..cb084b1c166 100644 --- a/src/app/components/kbn.js +++ b/src/app/components/kbn.js @@ -7,6 +7,7 @@ function($, _, moment) { 'use strict'; var kbn = {}; + kbn.valueFormats = {}; kbn.round_interval = function(interval) { switch (true) { @@ -309,240 +310,27 @@ function($, _, moment) { ].join(';') + '">'; }; - kbn.byteFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1024) { - steps++; - size /= 1024; - } - - switch (steps) { - case 0: - ext = " B"; - break; - case 1: - ext = " KiB"; - break; - case 2: - ext = " MiB"; - break; - case 3: - ext = " GiB"; - break; - case 4: - ext = " TiB"; - break; - case 5: - ext = " PiB"; - break; - case 6: - ext = " EiB"; - break; - case 7: - ext = " ZiB"; - break; - case 8: - ext = " YiB"; - break; - } - - return (size.toFixed(decimals) + ext); + kbn.valueFormats.percent = function(size, decimals) { + return kbn.toFixed(size, decimals) + '%'; }; - kbn.bitFormat = function(size, decimals) { - var ext, steps = 0; + kbn.formatFuncCreator = function(factor, extArray) { + return function(size, decimals, scaledDecimals) { + var steps = 0; - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } + while (Math.abs(size) >= factor) { + steps++; + size /= factor; + } + if (steps > 0) { + decimals = scaledDecimals + (3 * steps); + } - while (Math.abs(size) >= 1024) { - steps++; - size /= 1024; - } - - switch (steps) { - case 0: - ext = " b"; - break; - case 1: - ext = " Kib"; - break; - case 2: - ext = " Mib"; - break; - case 3: - ext = " Gib"; - break; - case 4: - ext = " Tib"; - break; - case 5: - ext = " Pib"; - break; - case 6: - ext = " Eib"; - break; - case 7: - ext = " Zib"; - break; - case 8: - ext = " Yib"; - break; - } - - return (size.toFixed(decimals) + ext); + return kbn.toFixed(size, decimals) + extArray[steps]; + }; }; - kbn.bpsFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1000) { - steps++; - size /= 1000; - } - - switch (steps) { - case 0: - ext = " bps"; - break; - case 1: - ext = " Kbps"; - break; - case 2: - ext = " Mbps"; - break; - case 3: - ext = " Gbps"; - break; - case 4: - ext = " Tbps"; - break; - case 5: - ext = " Pbps"; - break; - case 6: - ext = " Ebps"; - break; - case 7: - ext = " Zbps"; - break; - case 8: - ext = " Ybps"; - break; - } - - return (size.toFixed(decimals) + ext); - }; - - kbn.shortFormat = function(size, decimals) { - var ext, steps = 0; - - if(_.isUndefined(decimals)) { - decimals = 2; - } else if (decimals === 0) { - decimals = undefined; - } - - while (Math.abs(size) >= 1000) { - steps++; - size /= 1000; - } - - switch (steps) { - case 0: - ext = ""; - break; - case 1: - ext = " K"; - break; - case 2: - ext = " Mil"; - break; - case 3: - ext = " Bil"; - break; - case 4: - ext = " Tri"; - break; - case 5: - ext = " Quadr"; - break; - case 6: - ext = " Quint"; - break; - case 7: - ext = " Sext"; - break; - case 8: - ext = " Sept"; - break; - } - - return (size.toFixed(decimals) + ext); - }; - - kbn.getFormatFunction = function(formatName, decimals) { - switch(formatName) { - case 'short': - return function(val) { - return kbn.shortFormat(val, decimals); - }; - case 'bytes': - return function(val) { - return kbn.byteFormat(val, decimals); - }; - case 'bits': - return function(val) { - return kbn.bitFormat(val, decimals); - }; - case 'bps': - return function(val) { - return kbn.bpsFormat(val, decimals); - }; - case 's': - return function(val) { - return kbn.sFormat(val, decimals); - }; - case 'ms': - return function(val) { - return kbn.msFormat(val, decimals); - }; - case 'µs': - return function(val) { - return kbn.microsFormat(val, decimals); - }; - case 'ns': - return function(val) { - return kbn.nanosFormat(val, decimals); - }; - case 'percent': - return function(val, axis) { - return kbn.noneFormat(val, axis ? axis.tickDecimals : null) + ' %'; - }; - default: - return function(val, axis) { - return kbn.noneFormat(val, axis ? axis.tickDecimals : null); - }; - } - }; - - kbn.noneFormat = function(value, decimals) { + kbn.toFixed = function(value, decimals) { var factor = decimals ? Math.pow(10, decimals) : 1; var formatted = String(Math.round(value * factor) / factor); @@ -553,7 +341,6 @@ function($, _, moment) { // If tickDecimals was specified, ensure that we have exactly that // much precision; otherwise default to the value's own precision. - if (decimals != null) { var decimalPos = formatted.indexOf("."); var precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; @@ -565,97 +352,87 @@ function($, _, moment) { return formatted; }; - kbn.msFormat = function(size, decimals) { - // Less than 1 milli, downscale to micro - if (size !== 0 && Math.abs(size) < 1) { - return kbn.microsFormat(size * 1000, decimals); - } - else if (Math.abs(size) < 1000) { - return size.toFixed(decimals) + " ms"; + kbn.valueFormats.bits = kbn.formatFuncCreator(1024, [' b', ' Kib', ' Mib', ' Gib', ' Tib', ' Pib', ' Eib', ' Zib', ' Yib']); + kbn.valueFormats.bytes = kbn.formatFuncCreator(1024, [' B', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']); + kbn.valueFormats.bps = kbn.formatFuncCreator(1000, [' bps', ' Kbps', ' Mbps', ' Gbps', ' Tbps', ' Pbps', ' Ebps', ' Zbps', ' Ybps']); + kbn.valueFormats.short = kbn.formatFuncCreator(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Qaudr', ' Quint', ' Sext', ' Sept']); + kbn.valueFormats.none = kbn.toFixed; + + kbn.valueFormats.ms = function(size, decimals, scaledDecimals) { + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + " ms"; } // Less than 1 min else if (Math.abs(size) < 60000) { - return (size / 1000).toFixed(decimals) + " s"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " s"; } // Less than 1 hour, devide in minutes else if (Math.abs(size) < 3600000) { - return (size / 60000).toFixed(decimals) + " min"; + return kbn.toFixed(size / 60000, scaledDecimals + 5) + " min"; } // Less than one day, devide in hours else if (Math.abs(size) < 86400000) { - return (size / 3600000).toFixed(decimals) + " hour"; + return kbn.toFixed(size / 3600000, scaledDecimals + 7) + " hour"; } // Less than one year, devide in days else if (Math.abs(size) < 31536000000) { - return (size / 86400000).toFixed(decimals) + " day"; + return kbn.toFixed(size / 86400000, scaledDecimals + 8) + " day"; } - return (size / 31536000000).toFixed(decimals) + " year"; + return kbn.toFixed(size / 31536000000, scaledDecimals + 10) + " year"; }; - kbn.sFormat = function(size, decimals) { - // Less than 1 sec, downscale to milli - if (size !== 0 && Math.abs(size) < 1) { - return kbn.msFormat(size * 1000, decimals); - } - // Less than 10 min, use seconds - else if (Math.abs(size) < 600) { - return size.toFixed(decimals) + " s"; + kbn.valueFormats.s = function(size, decimals, scaledDecimals) { + if (Math.abs(size) < 600) { + return kbn.toFixed(size, decimals) + " s"; } // Less than 1 hour, devide in minutes else if (Math.abs(size) < 3600) { - return (size / 60).toFixed(decimals) + " min"; + return kbn.toFixed(size / 60, scaledDecimals + 1) + " min"; } // Less than one day, devide in hours else if (Math.abs(size) < 86400) { - return (size / 3600).toFixed(decimals) + " hour"; + return kbn.toFixed(size / 3600, scaledDecimals + 4) + " hour"; } // Less than one week, devide in days else if (Math.abs(size) < 604800) { - return (size / 86400).toFixed(decimals) + " day"; + return kbn.toFixed(size / 86400, scaledDecimals + 5) + " day"; } // Less than one year, devide in week else if (Math.abs(size) < 31536000) { - return (size / 604800).toFixed(decimals) + " week"; + return kbn.toFixed(size / 604800, scaledDecimals + 6) + " week"; } - return (size / 3.15569e7).toFixed(decimals) + " year"; + return kbn.toFixed(size / 3.15569e7, scaledDecimals + 7) + " year"; }; - kbn.microsFormat = function(size, decimals) { - // Less than 1 micro, downscale to nano - if (size !== 0 && Math.abs(size) < 1) { - return kbn.nanosFormat(size * 1000, decimals); - } - else if (Math.abs(size) < 1000) { - return size.toFixed(decimals) + " µs"; + kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) { + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + " µs"; } else if (Math.abs(size) < 1000000) { - return (size / 1000).toFixed(decimals) + " ms"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " ms"; } else { - return (size / 1000000).toFixed(decimals) + " s"; + return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " s"; } }; - kbn.nanosFormat = function(size, decimals) { - if (Math.abs(size) < 1) { - return size.toFixed(decimals) + " ns"; - } - else if (Math.abs(size) < 1000) { - return size.toFixed(0) + " ns"; + kbn.valueFormats.ns = function(size, decimals, scaledDecimals) { + if (Math.abs(size) < 1000) { + return kbn.toFixed(size, decimals) + " ns"; } else if (Math.abs(size) < 1000000) { - return (size / 1000).toFixed(decimals) + " µs"; + return kbn.toFixed(size / 1000, scaledDecimals + 3) + " µs"; } else if (Math.abs(size) < 1000000000) { - return (size / 1000000).toFixed(decimals) + " ms"; + return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " ms"; } else if (Math.abs(size) < 60000000000){ - return (size / 1000000000).toFixed(decimals) + " s"; + return kbn.toFixed(size / 1000000000, scaledDecimals + 9) + " s"; } else { - return (size / 60000000000).toFixed(decimals) + " m"; + return kbn.toFixed(size / 60000000000, scaledDecimals + 12) + " m"; } }; diff --git a/src/app/components/require.config.js b/src/app/components/require.config.js index 1c6cf42e365..9e0c271a40f 100644 --- a/src/app/components/require.config.js +++ b/src/app/components/require.config.js @@ -41,6 +41,7 @@ require.config({ 'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack', 'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent', 'jquery.flot.time': '../vendor/jquery/jquery.flot.time', + 'jquery.flot.crosshair': '../vendor/jquery/jquery.flot.crosshair', modernizr: '../vendor/modernizr-2.6.1', @@ -84,6 +85,7 @@ require.config({ 'jquery.flot.stack': ['jquery', 'jquery.flot'], 'jquery.flot.stackpercent':['jquery', 'jquery.flot'], 'jquery.flot.time': ['jquery', 'jquery.flot'], + 'jquery.flot.crosshair':['jquery', 'jquery.flot'], 'angular-cookies': ['angular'], 'angular-dragdrop': ['jquery','jquery-ui','angular'], 'angular-loader': ['angular'], diff --git a/src/app/components/timeSeries.js b/src/app/components/timeSeries.js index 4c58c211cc3..a448a93649c 100644 --- a/src/app/components/timeSeries.js +++ b/src/app/components/timeSeries.js @@ -54,7 +54,7 @@ function (_, kbn) { } }; - TimeSeries.prototype.getFlotPairs = function (fillStyle, yFormats) { + TimeSeries.prototype.getFlotPairs = function (fillStyle) { var result = []; this.color = this.info.color; @@ -100,21 +100,21 @@ function (_, kbn) { } if (result.length) { - this.info.avg = (this.info.total / result.length); this.info.current = result[result.length-1][1]; - - var formater = kbn.getFormatFunction(yFormats[this.yaxis - 1], 2); - this.info.avg = this.info.avg != null ? formater(this.info.avg) : null; - this.info.current = this.info.current != null ? formater(this.info.current) : null; - this.info.min = this.info.min != null ? formater(this.info.min) : null; - this.info.max = this.info.max != null ? formater(this.info.max) : null; - this.info.total = this.info.total != null ? formater(this.info.total) : null; } return result; }; + TimeSeries.prototype.updateLegendValues = function(formater, decimals, scaledDecimals) { + this.info.avg = this.info.avg != null ? formater(this.info.avg, decimals, scaledDecimals) : null; + this.info.current = this.info.current != null ? formater(this.info.current, decimals, scaledDecimals) : null; + this.info.min = this.info.min != null ? formater(this.info.min, decimals, scaledDecimals) : null; + this.info.max = this.info.max != null ? formater(this.info.max, decimals, scaledDecimals) : null; + this.info.total = this.info.total != null ? formater(this.info.total, decimals, scaledDecimals) : null; + }; + return TimeSeries; }); diff --git a/src/app/controllers/all.js b/src/app/controllers/all.js index f5b943e36c6..351060fbb8c 100644 --- a/src/app/controllers/all.js +++ b/src/app/controllers/all.js @@ -15,5 +15,6 @@ define([ './opentsdbTargetCtrl', './annotationsEditorCtrl', './templateEditorCtrl', + './sharePanelCtrl', './jsonEditorCtrl', ], function () {}); diff --git a/src/app/controllers/dashboardCtrl.js b/src/app/controllers/dashboardCtrl.js index 424f0e225e8..dfe97682aa0 100644 --- a/src/app/controllers/dashboardCtrl.js +++ b/src/app/controllers/dashboardCtrl.js @@ -19,19 +19,18 @@ function (angular, $, config, _) { dashboardSrv, dashboardViewStateSrv, panelMoveSrv, - timer, $timeout) { $scope.editor = { index: 0 }; $scope.panelNames = config.panels; var resizeEventTimeout; - $scope.init = function() { + this.init = function(dashboardData) { $scope.availablePanels = config.panels; - $scope.onAppEvent('setup-dashboard', $scope.setupDashboard); - $scope.onAppEvent('show-json-editor', $scope.showJsonEditor); $scope.reset_row(); $scope.registerWindowResizeEvent(); + $scope.onAppEvent('show-json-editor', $scope.showJsonEditor); + $scope.setupDashboard(dashboardData); }; $scope.registerWindowResizeEvent = function() { @@ -41,7 +40,7 @@ function (angular, $, config, _) { }); }; - $scope.setupDashboard = function(event, dashboardData) { + $scope.setupDashboard = function(dashboardData) { $rootScope.performance.dashboardLoadStart = new Date().getTime(); $rootScope.performance.panelsInitialized = 0; $rootScope.performance.panelsRendered = 0; @@ -59,7 +58,7 @@ function (angular, $, config, _) { $scope.setWindowTitleAndTheme(); - $scope.emitAppEvent("dashboard-loaded", $scope.dashboard); + $scope.appEvent("dashboard-loaded", $scope.dashboard); }; $scope.setWindowTitleAndTheme = function() { @@ -114,7 +113,7 @@ function (angular, $, config, _) { var editScope = $rootScope.$new(); editScope.object = options.object; editScope.updateHandler = options.updateHandler; - $scope.emitAppEvent('show-dash-editor', { src: 'app/partials/edit_json.html', scope: editScope }); + $scope.appEvent('show-dash-editor', { src: 'app/partials/edit_json.html', scope: editScope }); }; $scope.checkFeatureToggles = function() { @@ -129,6 +128,5 @@ function (angular, $, config, _) { return $scope.editorTabs; }; - $scope.init(); }); }); diff --git a/src/app/controllers/dashboardNavCtrl.js b/src/app/controllers/dashboardNavCtrl.js index 76ef34f8e92..084d1e215a6 100644 --- a/src/app/controllers/dashboardNavCtrl.js +++ b/src/app/controllers/dashboardNavCtrl.js @@ -69,7 +69,7 @@ function (angular, _, moment, config, store) { }; $scope.openSearch = function() { - $scope.emitAppEvent('show-dash-editor', { src: 'app/partials/search.html' }); + $scope.appEvent('show-dash-editor', { src: 'app/partials/search.html' }); }; $scope.saveDashboard = function() { @@ -78,7 +78,7 @@ function (angular, _, moment, config, store) { var clone = angular.copy($scope.dashboard); $scope.db.saveDashboard(clone) .then(function(result) { - alertSrv.set('Dashboard Saved', 'Saved as "' + result.title + '"','success', 3000); + $scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + result.title]); if (result.url !== $location.path()) { $location.search({}); @@ -88,12 +88,12 @@ function (angular, _, moment, config, store) { $rootScope.$emit('dashboard-saved', $scope.dashboard); }, function(err) { - alertSrv.set('Save failed', err, 'error', 5000); + $scope.appEvent('alert-error', ['Save failed', err]); }); }; $scope.deleteDashboard = function(evt, options) { - if (!confirm('Are you sure you want to delete dashboard?')) { + if (!confirm('Do you want to delete dashboard ' + options.title + ' ?')) { return; } @@ -101,9 +101,9 @@ function (angular, _, moment, config, store) { var id = options.id; $scope.db.deleteDashboard(id).then(function(id) { - alertSrv.set('Dashboard Deleted', id + ' has been deleted', 'success', 5000); - }, function() { - alertSrv.set('Dashboard Not Deleted', 'An error occurred deleting the dashboard', 'error', 5000); + $scope.appEvent('alert-success', ['Dashboard Deleted', id + ' has been deleted']); + }, function(err) { + $scope.appEvent('alert-error', ['Deleted failed', err]); }); }; @@ -138,7 +138,7 @@ function (angular, _, moment, config, store) { }; $scope.editJson = function() { - $scope.emitAppEvent('show-json-editor', { object: $scope.dashboard }); + $scope.appEvent('show-json-editor', { object: $scope.dashboard }); }; $scope.openSaveDropdown = function() { diff --git a/src/app/controllers/grafanaCtrl.js b/src/app/controllers/grafanaCtrl.js index 060c0bc0803..3a420c37d3f 100644 --- a/src/app/controllers/grafanaCtrl.js +++ b/src/app/controllers/grafanaCtrl.js @@ -10,19 +10,19 @@ function (angular, config, _, $, store) { var module = angular.module('grafana.controllers'); - module.controller('GrafanaCtrl', function($scope, alertSrv, grafanaVersion, $rootScope) { + module.controller('GrafanaCtrl', function($scope, alertSrv, utilSrv, grafanaVersion, $rootScope, $controller) { $scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion; - $scope.consoleEnabled = store.getBool('grafanaConsole'); - + $scope._ = _; $rootScope.profilingEnabled = store.getBool('profilingEnabled'); $rootScope.performance = { loadStart: new Date().getTime() }; $scope.init = function() { - $scope._ = _; - if ($rootScope.profilingEnabled) { $scope.initProfiling(); } + alertSrv.init(); + utilSrv.init(); + $scope.dashAlerts = alertSrv; $scope.grafana = { style: 'dark' }; }; @@ -32,12 +32,16 @@ function (angular, config, _, $, store) { store.set('grafanaConsole', $scope.consoleEnabled); }; + $scope.initDashboard = function(dashboardData, viewScope) { + $controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData); + }; + $rootScope.onAppEvent = function(name, callback) { var unbind = $rootScope.$on(name, callback); this.$on('$destroy', unbind); }; - $rootScope.emitAppEvent = function(name, payload) { + $rootScope.appEvent = function(name, payload) { $rootScope.$emit(name, payload); }; diff --git a/src/app/controllers/graphiteImport.js b/src/app/controllers/graphiteImport.js index d60c8ada3be..091f4b8fe5f 100644 --- a/src/app/controllers/graphiteImport.js +++ b/src/app/controllers/graphiteImport.js @@ -1,14 +1,15 @@ define([ 'angular', 'app', - 'lodash' + 'lodash', + 'kbn' ], -function (angular, app, _) { +function (angular, app, _, kbn) { 'use strict'; var module = angular.module('grafana.controllers'); - module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv) { + module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv, $location) { $scope.init = function() { $scope.datasources = datasourceSrv.getMetricSources(); @@ -72,18 +73,19 @@ function (angular, app, _) { newDashboard.title = state.name; newDashboard.rows.push(currentRow); - _.each(state.graphs, function(graph) { + _.each(state.graphs, function(graph, index) { if (currentRow.panels.length === graphsPerRow) { currentRow = angular.copy(rowTemplate); newDashboard.rows.push(currentRow); } panel = { - type: 'graphite', + type: 'graph', span: 12 / graphsPerRow, title: graph[1].title, targets: [], - datasource: datasource + datasource: datasource, + id: index + 1 }; _.each(graph[1].target, function(target) { @@ -95,7 +97,9 @@ function (angular, app, _) { currentRow.panels.push(panel); }); - $scope.emitAppEvent('setup-dashboard', newDashboard); + window.grafanaImportDashboard = newDashboard; + $location.path('/dashboard/import/' + kbn.slugifyForUrl(newDashboard.title)); + $scope.dismiss(); } diff --git a/src/app/controllers/row.js b/src/app/controllers/row.js index 621c3eddda5..55a838826bc 100644 --- a/src/app/controllers/row.js +++ b/src/app/controllers/row.js @@ -13,6 +13,7 @@ function (angular, app, _) { title: "Row", height: "150px", collapse: false, + editable: true, panels: [], }; @@ -22,6 +23,11 @@ function (angular, app, _) { $scope.reset_panel(); }; + $scope.togglePanelMenu = function(posX) { + $scope.showPanelMenu = !$scope.showPanelMenu; + $scope.panelMenuPos = posX; + }; + $scope.toggle_row = function(row) { row.collapse = row.collapse ? false : true; if (!row.collapse) { diff --git a/src/app/controllers/search.js b/src/app/controllers/search.js index 18b860d0ab5..4977ced75af 100644 --- a/src/app/controllers/search.js +++ b/src/app/controllers/search.js @@ -29,7 +29,7 @@ function (angular, _, config, $) { $scope.keyDown = function (evt) { if (evt.keyCode === 27) { - $scope.emitAppEvent('hide-dash-editor'); + $scope.appEvent('hide-dash-editor'); } if (evt.keyCode === 40) { $scope.moveSelection(1); @@ -62,6 +62,7 @@ function (angular, _, config, $) { }; $scope.goToDashboard = function(id) { + $location.search({}); $location.path("/dashboard/db/" + id); }; @@ -121,7 +122,7 @@ function (angular, _, config, $) { $scope.deleteDashboard = function(dash, evt) { evt.stopPropagation(); - $scope.emitAppEvent('delete-dashboard', { id: dash.id }); + $scope.appEvent('delete-dashboard', { id: dash.id, title: dash.title }); $scope.results.dashboards = _.without($scope.results.dashboards, dash); }; diff --git a/src/app/controllers/sharePanelCtrl.js b/src/app/controllers/sharePanelCtrl.js new file mode 100644 index 00000000000..e52dc297152 --- /dev/null +++ b/src/app/controllers/sharePanelCtrl.js @@ -0,0 +1,90 @@ +define([ + 'angular', + 'lodash' +], +function (angular, _) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('SharePanelCtrl', function($scope, $location, $timeout, timeSrv, $element, templateSrv) { + + $scope.init = function() { + $scope.editor = { index: 0 }; + $scope.forCurrent = true; + $scope.toPanel = true; + $scope.includeTemplateVars = true; + + $scope.buildUrl(); + }; + + $scope.buildUrl = function() { + var baseUrl = $location.absUrl(); + var queryStart = baseUrl.indexOf('?'); + + if (queryStart !== -1) { + baseUrl = baseUrl.substring(0, queryStart); + } + + var panelId = $scope.panel.id; + var range = timeSrv.timeRange(false); + var params = angular.copy($location.search()); + + if (_.isString(range.to) && range.to.indexOf('now')) { + range = timeSrv.timeRange(); + } + + params.from = range.from; + params.to = range.to; + + if (_.isDate(params.from)) { params.from = params.from.getTime(); } + if (_.isDate(params.to)) { params.to = params.to.getTime(); } + + if ($scope.includeTemplateVars) { + _.each(templateSrv.variables, function(variable) { + params['var-' + variable.name] = variable.current.text; + }); + } + else { + _.each(templateSrv.variables, function(variable) { + delete params['var-' + variable.name]; + }); + } + + if (!$scope.forCurrent) { + delete params.from; + delete params.to; + } + + if ($scope.toPanel) { + params.panelId = panelId; + params.fullscreen = true; + } else { + delete params.panelId; + delete params.fullscreen; + } + + var paramsArray = []; + _.each(params, function(value, key) { + var str = key; + if (value !== true) { + str += '=' + encodeURIComponent(value); + } + paramsArray.push(str); + }); + + $scope.shareUrl = baseUrl + "?" + paramsArray.join('&') ; + + $timeout(function() { + var input = $element.find('[data-share-panel-url]'); + input.focus(); + input.select(); + }, 10); + + }; + + $scope.init(); + + }); + +}); diff --git a/src/app/controllers/submenuCtrl.js b/src/app/controllers/submenuCtrl.js index a1067ee70ac..bf71377dd14 100644 --- a/src/app/controllers/submenuCtrl.js +++ b/src/app/controllers/submenuCtrl.js @@ -1,9 +1,8 @@ define([ 'angular', - 'app', 'lodash' ], -function (angular, app, _) { +function (angular, _) { 'use strict'; var module = angular.module('grafana.controllers'); diff --git a/src/app/controllers/templateEditorCtrl.js b/src/app/controllers/templateEditorCtrl.js index 058a190658b..394946447a7 100644 --- a/src/app/controllers/templateEditorCtrl.js +++ b/src/app/controllers/templateEditorCtrl.js @@ -72,6 +72,9 @@ function (angular, _) { if ($scope.current.type === 'interval') { $scope.current.query = '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d'; } + if ($scope.current.type === 'query') { + $scope.current.query = ''; + } }; $scope.removeVariable = function(variable) { diff --git a/src/app/directives/dashEditLink.js b/src/app/directives/dashEditLink.js index 599cf341fe5..b0ac97d3423 100644 --- a/src/app/directives/dashEditLink.js +++ b/src/app/directives/dashEditLink.js @@ -16,7 +16,7 @@ function (angular, $) { elem.bind('click',function() { $timeout(function() { var editorScope = attrs.editorScope === 'isolated' ? null : scope; - scope.emitAppEvent('show-dash-editor', { src: partial, scope: editorScope }); + scope.appEvent('show-dash-editor', { src: partial, scope: editorScope }); }); }); } @@ -34,6 +34,7 @@ function (angular, $) { function hideScrollbars(value) { if (value) { + window.scrollTo(0,0); document.documentElement.style.overflow = 'hidden'; // firefox, chrome document.body.scroll = "no"; // ie only } else { diff --git a/src/app/directives/dashUpload.js b/src/app/directives/dashUpload.js index 1d7c4ec405e..ba214cf19a4 100644 --- a/src/app/directives/dashUpload.js +++ b/src/app/directives/dashUpload.js @@ -1,12 +1,13 @@ define([ - 'angular' + 'angular', + 'kbn' ], -function (angular) { +function (angular, kbn) { 'use strict'; var module = angular.module('grafana.directives'); - module.directive('dashUpload', function(timer, alertSrv) { + module.directive('dashUpload', function(timer, alertSrv, $location) { return { restrict: 'A', link: function(scope) { @@ -14,9 +15,10 @@ function (angular) { var files = evt.target.files; // FileList object var readerOnload = function() { return function(e) { - var dashboard = JSON.parse(e.target.result); scope.$apply(function() { - scope.emitAppEvent('setup-dashboard', dashboard); + window.grafanaImportDashboard = JSON.parse(e.target.result); + var title = kbn.slugifyForUrl(window.grafanaImportDashboard.title); + $location.path('/dashboard/import/' + title); }); }; }; diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index 15f1556aa62..42ef1b91cb5 100755 --- a/src/app/directives/grafanaGraph.js +++ b/src/app/directives/grafanaGraph.js @@ -3,9 +3,10 @@ define([ 'jquery', 'kbn', 'moment', - 'lodash' + 'lodash', + './grafanaGraph.tooltip' ], -function (angular, $, kbn, moment, _) { +function (angular, $, kbn, moment, _, graphTooltip) { 'use strict'; var module = angular.module('grafana.directives'); @@ -15,23 +16,15 @@ function (angular, $, kbn, moment, _) { restrict: 'A', template: '
', link: function(scope, elem) { - var data, annotations; - var hiddenData = {}; var dashboard = scope.dashboard; + var data, annotations; var legendSideLastValue = null; scope.$on('refresh',function() { scope.get_data(); }); - scope.$on('toggleLegend', function(e, series) { - _.each(series, function(serie) { - if (hiddenData[serie.alias]) { - data.push(hiddenData[serie.alias]); - delete hiddenData[serie.alias]; - } - }); - + scope.$on('toggleLegend', function() { render_panel(); }); @@ -88,6 +81,18 @@ function (angular, $, kbn, moment, _) { } } + function updateLegendValues(plot) { + var yaxis = plot.getYAxes(); + + for (var i = 0; i < data.length; i++) { + var series = data[i]; + var axis = yaxis[series.yaxis - 1]; + var formater = kbn.valueFormats[scope.panel.y_formats[series.yaxis - 1]]; + series.updateLegendValues(formater, axis.tickDecimals, axis.scaledDecimals); + } + + } + // Function for rendering panel function render_panel() { if (shouldAbortRender()) { @@ -95,21 +100,11 @@ function (angular, $, kbn, moment, _) { } var panel = scope.panel; - - _.each(_.keys(scope.hiddenSeries), function(seriesAlias) { - var dataSeries = _.find(data, function(series) { - return series.info.alias === seriesAlias; - }); - if (dataSeries) { - hiddenData[dataSeries.info.alias] = dataSeries; - data = _.without(data, dataSeries); - } - }); - var stack = panel.stack ? true : null; // Populate element var options = { + hooks: { draw: [updateLegendValues] }, legend: { show: false }, series: { stackpercent: panel.stack ? panel.percentage : false, @@ -132,7 +127,8 @@ function (angular, $, kbn, moment, _) { show: panel.points, fill: 1, fillColor: false, - radius: panel.pointradius + radius: panel.points ? panel.pointradius : 2 + // little points when highlight points }, shadowSize: 1 }, @@ -149,6 +145,9 @@ function (angular, $, kbn, moment, _) { selection: { mode: "x", color: '#666' + }, + crosshair: { + mode: panel.tooltip.shared ? "x" : null } }; @@ -156,6 +155,11 @@ function (angular, $, kbn, moment, _) { var series = data[i]; series.applySeriesOverrides(panel.seriesOverrides); series.data = series.getFlotPairs(panel.nullPointMode, panel.y_formats); + // if hidden remove points and disable stack + if (scope.hiddenSeries[series.info.alias]) { + series.data = []; + series.stack = false; + } } if (data.length && data[0].info.timeStep) { @@ -313,7 +317,9 @@ function (angular, $, kbn, moment, _) { } function configureAxisMode(axis, format) { - axis.tickFormatter = kbn.getFormatFunction(format, 1); + axis.tickFormatter = function(val, axis) { + return kbn.valueFormats[format](val, axis.tickDecimals, axis.scaledDecimals); + }; } function time_format(interval, ticks, min, max) { @@ -338,40 +344,6 @@ function (angular, $, kbn, moment, _) { return "%H:%M"; } - var $tooltip = $('
'); - - elem.bind("plothover", function (event, pos, item) { - var group, value, timestamp, seriesInfo, format; - - if (item) { - seriesInfo = item.series.info; - format = scope.panel.y_formats[seriesInfo.yaxis - 1]; - - if (seriesInfo.alias) { - group = '' + - '' + ' ' + - seriesInfo.alias + - '
'; - } else { - group = kbn.query_color_dot(item.series.color, 15) + ' '; - } - - if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') { - value = item.datapoint[1] - item.datapoint[2]; - } - else { - value = item.datapoint[1]; - } - - value = kbn.getFormatFunction(format, 2)(value, item.series.yaxis); - timestamp = dashboard.formatDate(item.datapoint[0]); - - $tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY); - } else { - $tooltip.detach(); - } - }); - function render_panel_as_graphite_png(url) { url += '&width=' + elem.width(); url += '&height=' + elem.css('height').replace('px', ''); @@ -422,6 +394,8 @@ function (angular, $, kbn, moment, _) { elem.html(''); } + graphTooltip.register(elem, dashboard, scope); + elem.bind("plotselected", function (event, ranges) { scope.$apply(function() { timeSrv.setTime({ diff --git a/src/app/directives/grafanaGraph.tooltip.js b/src/app/directives/grafanaGraph.tooltip.js new file mode 100644 index 00000000000..513d7126985 --- /dev/null +++ b/src/app/directives/grafanaGraph.tooltip.js @@ -0,0 +1,122 @@ +define([ + 'jquery', + 'kbn', +], +function ($, kbn) { + 'use strict'; + + function registerTooltipFeatures(elem, dashboard, scope) { + + var $tooltip = $('
'); + + elem.mouseleave(function () { + if(scope.panel.tooltip.shared) { + var plot = elem.data().plot; + $tooltip.detach(); + plot.clearCrosshair(); + plot.unhighlight(); + } + }); + + function findHoverIndex(posX, series) { + for (var j = 0; j < series.data.length; j++) { + if (series.data[j][0] > posX) { + return Math.max(j - 1, 0); + } + } + return j - 1; + } + + elem.bind("plothover", function (event, pos, item) { + var plot = elem.data().plot; + var data = plot.getData(); + var group, value, timestamp, seriesInfo, format, i, series, hoverIndex, seriesHtml; + + if (scope.panel.tooltip.shared) { + plot.unhighlight(); + + //check if all series has same length if so, only one x index will + //be checked and only for exact timestamp values + var pointCount = data[0].data.length; + for (i = 1; i < data.length; i++) { + if (data[i].data.length !== pointCount) { + console.log('WARNING: tootltip shared can not be shown becouse of series points do not align, different point counts'); + $tooltip.detach(); + return; + } + } + + seriesHtml = ''; + series = data[0]; + hoverIndex = findHoverIndex(pos.x, series); + + //now we know the current X (j) position for X and Y values + timestamp = dashboard.formatDate(series.data[hoverIndex][0]); + var last_value = 0; //needed for stacked values + + for (i = data.length-1; i >= 0; --i) { + //stacked values should be added in reverse order + series = data[i]; + seriesInfo = series.info; + format = scope.panel.y_formats[seriesInfo.yaxis - 1]; + + if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') { + value = series.data[hoverIndex][1]; + } else { + last_value += series.data[hoverIndex][1]; + value = last_value; + } + + value = kbn.valueFormats[format](value, series.yaxis.tickDecimals); + + if (seriesInfo.alias) { + group = ' ' + seriesInfo.alias; + } else { + group = kbn.query_color_dot(series.color, 15) + ' '; + } + + //pre-pending new values + seriesHtml = group + ': ' + value + '
' + seriesHtml; + + plot.highlight(i, hoverIndex); + } + + $tooltip.html('
'+ timestamp + '
' + seriesHtml + '
') + .place_tt(pos.pageX + 20, pos.pageY); + return; + } + if (item) { + seriesInfo = item.series.info; + format = scope.panel.y_formats[seriesInfo.yaxis - 1]; + + if (seriesInfo.alias) { + group = '' + + '' + ' ' + + seriesInfo.alias + + '
'; + } else { + group = kbn.query_color_dot(item.series.color, 15) + ' '; + } + + if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') { + value = item.datapoint[1] - item.datapoint[2]; + } + else { + value = item.datapoint[1]; + } + + value = kbn.valueFormats[format](value, item.series.yaxis.tickDecimals); + timestamp = dashboard.formatDate(item.datapoint[0]); + + $tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY); + } else { + $tooltip.detach(); + } + }); + + } + + return { + register: registerTooltipFeatures + }; +}); diff --git a/src/app/directives/grafanaPanel.js b/src/app/directives/grafanaPanel.js index 5f56fd67b35..2a8493727bc 100644 --- a/src/app/directives/grafanaPanel.js +++ b/src/app/directives/grafanaPanel.js @@ -1,7 +1,7 @@ define([ 'angular', 'jquery', - 'lodash', + './panelMenu', ], function (angular, $) { 'use strict'; @@ -15,37 +15,19 @@ function (angular, $) { var panelHeader = '
'+ - '
' + - '
' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + '' + + '' + - '' + - '' + - '' + + '' + + '' + + '' + - '' + - '' + - '{{panel.title | interpolateTemplateVars}}' + - '' + - ''+ - - '
'+ - '
\n'+ + '
' + + '
'+ '
'; return { diff --git a/src/app/directives/panelMenu.js b/src/app/directives/panelMenu.js new file mode 100644 index 00000000000..aac7e04f989 --- /dev/null +++ b/src/app/directives/panelMenu.js @@ -0,0 +1,130 @@ +define([ + 'angular', + 'jquery', + 'lodash', +], +function (angular, $, _) { + 'use strict'; + + angular + .module('grafana.directives') + .directive('panelMenu', function($compile) { + var linkTemplate = '{{panel.title | interpolateTemplateVars}}'; + var moveAttributes = ' data-drag=true data-jqyoui-options="kbnJqUiDraggableOptions"'+ + ' jqyoui-draggable="{'+ + 'animate:false,'+ + 'mutate:false,'+ + 'index:{{$index}},'+ + 'onStart:\'panelMoveStart\','+ + 'onStop:\'panelMoveStop\''+ + '}" ng-model="panel" '; + + function createMenuTemplate($scope) { + var template = '
'; + template += '
'; + template += '
'; + template += ''; + template += ''; + template += ''; + template += ''; + template += '
'; + template += '
'; + + template += '
'; + + _.each($scope.panelMeta.menu, function(item) { + template += ''; + }); + + template += '
'; + template += '
'; + template += '
'; + return template; + } + + return { + restrict: 'A', + link: function($scope, elem) { + var $link = $(linkTemplate); + var $panelContainer = elem.parents(".panel-container"); + var menuWidth = $scope.panelMeta.menu.length === 5 ? 246 : 201; + var menuScope = null; + var timeout = null; + var $menu = null; + + elem.append($link); + + function dismiss(time) { + clearTimeout(timeout); + timeout = null; + + if (time) { + timeout = setTimeout(dismiss, time); + return; + } + + // if hovering or draging pospone close + if ($menu.is(':hover') || $scope.dashboard.$$panelDragging) { + dismiss(2500); + return; + } + + if (menuScope) { + $menu.unbind(); + $menu.remove(); + menuScope.$destroy(); + menuScope = null; + $menu = null; + $panelContainer.removeClass('panel-highlight'); + } + } + + var showMenu = function() { + if ($menu) { + dismiss(); + return; + } + + var windowWidth = $(window).width(); + var panelLeftPos = $(elem).offset().left; + var panelWidth = $(elem).width(); + var menuLeftPos = (panelWidth / 2) - (menuWidth/2); + var stickingOut = panelLeftPos + menuLeftPos + menuWidth - windowWidth; + if (stickingOut > 0) { + menuLeftPos -= stickingOut + 10; + } + if (panelLeftPos + menuLeftPos < 0) { + menuLeftPos = 0; + } + + var menuTemplate = createMenuTemplate($scope); + $menu = $(menuTemplate); + $menu.css('left', menuLeftPos); + $menu.mouseleave(function() { + dismiss(1000); + }); + + menuScope = $scope.$new(); + + $('.panel-menu').remove(); + elem.append($menu); + $scope.$apply(function() { + $compile($menu.contents())(menuScope); + }); + + $(".panel-container").removeClass('panel-highlight'); + $panelContainer.toggleClass('panel-highlight'); + + dismiss(2500); + }; + + elem.click(showMenu); + $compile(elem.contents())($scope); + } + }; + }); +}); diff --git a/src/app/directives/tip.js b/src/app/directives/tip.js index 974ed98a637..ba1d373efb7 100644 --- a/src/app/directives/tip.js +++ b/src/app/directives/tip.js @@ -17,4 +17,27 @@ function (angular, kbn) { } }; }); + + angular + .module('grafana.directives') + .directive('editorOptBool', function($compile) { + return { + restrict: 'E', + link: function(scope, elem, attrs) { + var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : ''; + var tip = attrs.tip ? (' ' + attrs.tip + '') : ''; + var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : ''; + + var template = '
' + + ' ' + + '' + + ' '; + elem.replaceWith($compile(angular.element(template))(scope)); + } + }; + }); + }); diff --git a/src/app/panels/graph/axisEditor.html b/src/app/panels/graph/axisEditor.html index 9380cb99327..3aec233f6be 100644 --- a/src/app/panels/graph/axisEditor.html +++ b/src/app/panels/graph/axisEditor.html @@ -40,42 +40,19 @@
Legend styles
-
- -
-
- -
-
- -
-
- -
+ + + +
Legend values
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
- + + + + +
@@ -96,19 +73,13 @@
-
- -
+
Show Axes
-
- -
-
- -
+ +
diff --git a/src/app/panels/graph/module.html b/src/app/panels/graph/module.html index b4302bbd0f2..3a6e0c8a0d1 100644 --- a/src/app/panels/graph/module.html +++ b/src/app/panels/graph/module.html @@ -8,7 +8,7 @@ Datapoints outside time range Can be caused by timezone mismatch between browser and graphite server
-
+
diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js index aa6667ee154..bc15b851f9d 100644 --- a/src/app/panels/graph/module.js +++ b/src/app/panels/graph/module.js @@ -15,7 +15,8 @@ define([ 'jquery.flot.selection', 'jquery.flot.time', 'jquery.flot.stack', - 'jquery.flot.stackpercent' + 'jquery.flot.stackpercent', + 'jquery.flot.crosshair' ], function (angular, app, $, _, kbn, moment, TimeSeries) { 'use strict'; @@ -160,7 +161,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries) { tooltip : { value_type: 'cumulative', - query_as_alias: true + shared: false, }, targets: [{}], diff --git a/src/app/panels/graph/styleEditor.html b/src/app/panels/graph/styleEditor.html index cd83f23f197..345e4165f96 100644 --- a/src/app/panels/graph/styleEditor.html +++ b/src/app/panels/graph/styleEditor.html @@ -1,15 +1,9 @@
Chart Options
-
- -
-
- -
-
- -
+ + +
@@ -30,19 +24,15 @@
-
- -
+ +
Multiple Series
-
- -
-
- - -
+ + + +
@@ -61,8 +51,16 @@
+ +
+
Tooltip
+
+ +
+
+
Series specific overrides Regex match example: /server[0-3]/i
diff --git a/src/app/panels/text/module.js b/src/app/panels/text/module.js index e652b40a56b..d778e877031 100644 --- a/src/app/panels/text/module.js +++ b/src/app/panels/text/module.js @@ -20,6 +20,7 @@ function (angular, app, _, require) { // Set and populate defaults var _d = { + title: 'default title', mode : "markdown", // 'html', 'markdown', 'text' content : "", style: {}, diff --git a/src/app/panels/timepicker/module.html b/src/app/panels/timepicker/module.html index 8357f66a7a3..9371eb16f2e 100644 --- a/src/app/panels/timepicker/module.html +++ b/src/app/panels/timepicker/module.html @@ -9,9 +9,7 @@ border: 0px !important; } - -
+ -
diff --git a/src/app/panels/timepicker/module.js b/src/app/panels/timepicker/module.js index 656af0898a0..c04a1910bf4 100644 --- a/src/app/panels/timepicker/module.js +++ b/src/app/panels/timepicker/module.js @@ -79,7 +79,7 @@ function (angular, app, _, moment, kbn) { $scope.temptime.to.date = moment($scope.temptime.to.date).add('days',1).toDate(); } - $scope.emitAppEvent('show-dash-editor', {src: 'app/panels/timepicker/custom.html', scope: $scope }); + $scope.appEvent('show-dash-editor', {src: 'app/panels/timepicker/custom.html', scope: $scope }); }; // Constantly validate the input of the fields. This function does not change any date variables diff --git a/src/app/partials/annotations_editor.html b/src/app/partials/annotations_editor.html index c72194b6f6a..a470dcb082e 100644 --- a/src/app/partials/annotations_editor.html +++ b/src/app/partials/annotations_editor.html @@ -61,10 +61,7 @@
-
- - -
+
diff --git a/src/app/partials/dashboard.html b/src/app/partials/dashboard.html index f619a74dc85..73d264ac1ce 100644 --- a/src/app/partials/dashboard.html +++ b/src/app/partials/dashboard.html @@ -1,4 +1,4 @@ -
+
@@ -74,9 +74,17 @@
-
+
+
+ + + + + + +
Time correction
-
- - -
+
@@ -42,7 +39,6 @@ Press enter to a add tag
-
@@ -71,16 +67,12 @@
-
- - -
-
- - -
-
- + + +
+ + +
diff --git a/src/app/partials/graphite/editor.html b/src/app/partials/graphite/editor.html index dcbc4306e0f..f54c830d46b 100755 --- a/src/app/partials/graphite/editor.html +++ b/src/app/partials/graphite/editor.html @@ -76,6 +76,7 @@
+
  • @@ -125,6 +126,7 @@
+
diff --git a/src/app/partials/import.html b/src/app/partials/import.html index f81468465d4..e89aff41342 100644 --- a/src/app/partials/import.html +++ b/src/app/partials/import.html @@ -16,11 +16,15 @@
-
- +
+
- + +
- {{dash.name}}{{dash.name}} + + import + +
diff --git a/src/app/partials/playlist.html b/src/app/partials/playlist.html index 1517678e730..b3be57e8fda 100644 --- a/src/app/partials/playlist.html +++ b/src/app/partials/playlist.html @@ -22,7 +22,8 @@ {{dashboard.title}} - + + diff --git a/src/app/partials/roweditor.html b/src/app/partials/roweditor.html index 032163ea64b..57060ebf93c 100644 --- a/src/app/partials/roweditor.html +++ b/src/app/partials/roweditor.html @@ -20,12 +20,8 @@
-
- -
-
- -
+ +
diff --git a/src/app/partials/share-panel.html b/src/app/partials/share-panel.html new file mode 100644 index 00000000000..d2c85e423ce --- /dev/null +++ b/src/app/partials/share-panel.html @@ -0,0 +1,33 @@ +
+ + + + + +
diff --git a/src/app/partials/submenu.html b/src/app/partials/submenu.html index 42884a84058..e82e2505afb 100644 --- a/src/app/partials/submenu.html +++ b/src/app/partials/submenu.html @@ -17,9 +17,6 @@
-
- - -
+ +
@@ -80,10 +80,7 @@
-
- - -
+
@@ -118,10 +115,7 @@
-
- - -
+
diff --git a/src/app/partials/unsaved-changes.html b/src/app/partials/unsaved-changes.html index b025575e7e7..846a30bbc3b 100644 --- a/src/app/partials/unsaved-changes.html +++ b/src/app/partials/unsaved-changes.html @@ -1,19 +1,18 @@ -