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: '
| - | {{dash.name}} | +{{dash.name}} | ++ + import + + |