Commit 1e81f8ec by Torkel Ödegaard

Merge branch 'master' of github.com:torkelo/grafana-private into pro

parents 28c5fc48 dac3cb15
......@@ -6,7 +6,8 @@
- [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)
=======
# 1.8.1 (2014-09-30)
**Fixes**
- [Issue #855](https://github.com/grafana/grafana/issues/855). Graph: Fix for scroll issue in graph edit mode when dropdown goes below screen
......
[Grafana](http://grafana.org) [![Build Status](https://api.travis-ci.org/grafana/grafana.svg)](https://travis-ci.org/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana)
[Grafana](http://grafana.org) [![Build Status](https://api.travis-ci.org/grafana/grafana.svg)](https://travis-ci.org/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/grafana/grafana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
================
[Website](http://grafana.org) |
[Twitter](http://twitter.com/grafana) |
......
......@@ -81,12 +81,8 @@ function (angular, config, _, $, store) {
$scope.initProfiling = function() {
var count = 0;
$scope.$watch(function digestCounter() {
count++;
}, function() {
});
$scope.onAppEvent('setup-dashboard', function() {
$scope.$watch(function digestCounter() { count++; }, function() { });
$scope.onAppEvent('dashboard-loaded', function() {
count = 0;
setTimeout(function() {
......
......@@ -17,7 +17,7 @@
"editable": true,
"type": "text",
"mode": "html",
"content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"//grafana.org/assets/img/logo_transparent_200x75.png\"> \n</div>",
"content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"img/logo_transparent_200x.png\"> \n</div>",
"style": {},
"title": "Welcome to"
}
......
......@@ -16,7 +16,13 @@ function (angular, kbn) {
var readerOnload = function() {
return function(e) {
scope.$apply(function() {
window.grafanaImportDashboard = JSON.parse(e.target.result);
try {
window.grafanaImportDashboard = JSON.parse(e.target.result);
} catch (err) {
console.log(err);
scope.appEvent('alert-error', ['Import failed', 'JSON -> JS Serialization failed: ' + err.message]);
return;
}
var title = kbn.slugifyForUrl(window.grafanaImportDashboard.title);
$location.path('/dashboard/import/' + title);
});
......
......@@ -29,6 +29,12 @@ function ($, kbn) {
return j - 1;
}
function showTooltip(title, innerHtml, pos) {
var body = '<div class="graph-tooltip small"><div class="graph-tooltip-time">'+ title + '</div> ' ;
body += innerHtml + '</div>';
$tooltip.html(body).place_tt(pos.pageX + 20, pos.pageY);
}
elem.bind("plothover", function (event, pos, item) {
var plot = elem.data().plot;
var data = plot.getData();
......@@ -46,8 +52,10 @@ function ($, kbn) {
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();
showTooltip('Shared tooltip error', '<ul>' +
'<li>Series point counts are not the same</li>' +
'<li>Set null point mode to null or null as zero</li>' +
'<li>For influxdb users set fill(0) in your query</li></ul>', pos);
return;
}
}
......@@ -91,22 +99,13 @@ function ($, kbn) {
plot.highlight(i, hoverIndex);
}
$tooltip.html('<div class="graph-tooltip small"><div class="graph-tooltip-time">'+ timestamp + '</div> ' + seriesHtml + '</div>')
.place_tt(pos.pageX + 20, pos.pageY);
showTooltip(timestamp, seriesHtml, pos);
return;
}
if (item) {
seriesInfo = item.series.info;
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
if (seriesInfo.alias) {
group = '<small style="font-size:0.9em;">' +
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
seriesInfo.alias +
'</small><br>';
} else {
group = kbn.query_color_dot(item.series.color, 15) + ' ';
}
group = '<i class="icon-minus" style="color:' + item.series.color +';"></i> ' + seriesInfo.alias;
if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') {
value = item.datapoint[1] - item.datapoint[2];
......@@ -117,8 +116,9 @@ function ($, kbn) {
value = kbn.valueFormats[format](value, item.series.yaxis.tickDecimals);
timestamp = dashboard.formatDate(item.datapoint[0]);
group += ': <span class="graph-tooltip-value">' + value + '</span>';
$tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY);
showTooltip(timestamp, group, pos);
} else {
$tooltip.detach();
}
......
......@@ -50,10 +50,10 @@
<div class="section">
<h5>Tooltip</h5>
<div class="editor-option">
<label class="small">shared <tip> Show all series values on the same time in the same tooltip and a x croshair to help follow all series</tip></label>
<input type="checkbox" ng-model="panel.tooltip.shared" ng-checked="panel.tooltip.shared" ng-change="render()">
</div>
<editor-opt-bool
text="All series" model="panel.tooltip.shared" change="render()"
tip="Show all series on same tooltip and a x croshair to help follow all series">
</editor-opt-bool>
<div class="editor-option" ng-show="panel.stack">
<label class="small">Stacked Values <tip>How should the values in stacked charts to be calculated?</tip></label>
<select class="input-small" ng-model="panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']" ng-change="render()"></select>
......
......@@ -67,8 +67,8 @@
<div ng-if="editor.index == 2">
<div class="editor-row">
<div class="section">
<editor-opt-bool text="Templating" model="dashboard.templating.enable"></editor-opt-bool>
<editor-opt-bool text="Annotations" model="dashboard.annotations.enable"></editor-opt-bool>
<editor-opt-bool text="Templating" model="dashboard.templating.enable" change="checkFeatureToggles()"></editor-opt-bool>
<editor-opt-bool text="Annotations" model="dashboard.annotations.enable" change="checkFeatureToggles()"></editor-opt-bool>
<div class="editor-option text-center" ng-repeat="pulldown in dashboard.nav">
<label class="small" style="text-transform:capitalize;">{{pulldown.type}}</label>
<input class="cr1" id="pulldown{{pulldown.type}}" type="checkbox" ng-model="pulldown.enable" ng-checked="pulldown.enable">
......
......@@ -31,7 +31,7 @@ function (angular) {
});
module.controller('DashFromDBProvider', function($scope, $rootScope, datasourceSrv, $routeParams, alertSrv) {
module.controller('DashFromDBProvider', function($scope, $rootScope, datasourceSrv, $routeParams) {
var db = datasourceSrv.getGrafanaDB();
var isTemp = window.location.href.indexOf('dashboard/temp') !== -1;
......@@ -41,14 +41,14 @@ function (angular) {
$scope.initDashboard(dashboard, $scope);
}).then(null, function(error) {
$scope.initDashboard({ title: 'Grafana'}, $scope);
alertSrv.set('Error', error, 'error');
$scope.appEvent('alert-error', ['Dashboard load failed', error]);
});
});
module.controller('DashFromImportCtrl', function($scope, $location, alertSrv) {
module.controller('DashFromImportCtrl', function($scope, $location) {
if (!window.grafanaImportDashboard) {
alertSrv.set('Not found', 'Cannot reload page with unsaved imported dashboard', 'warning', 7000);
$scope.appEvent('alert-warning', ['Dashboard load failed', 'Cannot reload unsaved imported dashboard']);
$location.path('');
return;
}
......
......@@ -31,8 +31,8 @@ function (angular, $, kbn, _, moment) {
this.rows = data.rows || [];
this.nav = data.nav || [];
this.time = data.time || { from: 'now-6h', to: 'now' };
this.templating = data.templating || { list: [], enable: false };
this.annotations = data.annotations || { list: [], enable: false};
this.templating = this._ensureListExist(data.templating);
this.annotations = this._ensureListExist(data.annotations);
this.refresh = data.refresh;
this.version = data.version || 0;
......@@ -40,11 +40,17 @@ function (angular, $, kbn, _, moment) {
this.nav.push({ type: 'timepicker' });
}
this.updateSchema(data);
this._updateSchema(data);
}
var p = DashboardModel.prototype;
p._ensureListExist = function (data) {
if (!data) { data = {}; }
if (!data.list) { data.list = []; }
return data;
};
p.getNextPanelId = function() {
var i, j, row, panel, max = 0;
for (i = 0; i < this.rows.length; i++) {
......@@ -116,7 +122,7 @@ function (angular, $, kbn, _, moment) {
$rootScope.$broadcast('refresh');
};
p.updateSchema = function(old) {
p._updateSchema = function(old) {
var i, j, k;
var oldVersion = this.version;
var panelUpgrades = [];
......
......@@ -178,4 +178,26 @@ define([
});
describe('when creating dashboard model with missing list for annoations or templating', function() {
var model;
beforeEach(module('grafana.services'));
beforeEach(inject(function(dashboardSrv) {
model = dashboardSrv.create({
annotations: {
enable: true,
},
templating: {
enable: true
}
});
}));
it('should add empty list', function() {
expect(model.annotations.list.length).to.be(0);
expect(model.templating.list.length).to.be(0);
});
});
});
define([
'kbn',
'lodash'
'kbn',
'lodash'
], function(kbn, _) {
'use strict';
......
/**
* @license AngularJS v1.2.21
* @license AngularJS v1.3.0-rc.3
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
......@@ -22,12 +22,12 @@
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider);
provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/**
* @ngdoc provider
* @name $routeProvider
* @kind function
*
* @description
*
......@@ -75,12 +75,12 @@ function $RouteProvider(){
*
* Object properties:
*
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
* published to scope under the `controllerAs` name.
* - `template` – `{string=|function()=}` – html template as a string or a function that
* - `template` – `{string=|function()=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
......@@ -90,7 +90,7 @@ function $RouteProvider(){
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
......@@ -108,7 +108,7 @@ function $RouteProvider(){
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
* - `key` `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
......@@ -116,7 +116,7 @@ function $RouteProvider(){
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` – {(string|function())=} – value to update
* - `redirectTo` – {(string|function())=} – value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
......@@ -216,10 +216,14 @@ function $RouteProvider(){
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object} params Mapping information to be assigned to `$route.current`.
* @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self
*/
this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params);
return this;
};
......@@ -230,10 +234,9 @@ function $RouteProvider(){
'$routeParams',
'$q',
'$injector',
'$http',
'$templateCache',
'$templateRequest',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/**
* @ngdoc service
......@@ -270,9 +273,6 @@ function $RouteProvider(){
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* Note that this example is using {@link ng.directive:script inlined templates}
* to get it working on jsfiddle as well.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
......@@ -441,6 +441,36 @@ function $RouteProvider(){
reload: function() {
forceReload = true;
$rootScope.$evalAsync(updateRoute);
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {Object} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
var searchParams = {}, self=this;
angular.forEach(Object.keys(newParams), function(key) {
if (!self.current.pathParams[key]) searchParams[key] = newParams[key];
});
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
$location.search(angular.extend({}, $location.search(), searchParams));
}
else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
}
};
......@@ -516,7 +546,7 @@ function $RouteProvider(){
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value);
$injector.get(value) : $injector.invoke(value, null, null, key);
});
if (angular.isDefined(template = next.template)) {
......@@ -530,8 +560,7 @@ function $RouteProvider(){
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
next.loadedTemplateUrl = templateUrl;
template = $http.get(templateUrl, {cache: $templateCache}).
then(function(response) { return response.data; });
template = $templateRequest(templateUrl);
}
}
if (angular.isDefined(template)) {
......@@ -693,7 +722,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
......@@ -771,7 +799,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
controllerAs: 'chapter'
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
}])
.controller('MainCtrl', ['$route', '$routeParams', '$location',
......@@ -844,7 +871,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement, function() {
$animate.leave(currentElement).then(function() {
previousElement = null;
});
previousElement = currentElement;
......@@ -867,7 +894,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter () {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
......@@ -922,4 +949,4 @@ function ngViewFillContentFactory($compile, $controller, $route) {
}
})(window, window.angular);
\ No newline at end of file
})(window, window.angular);
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -214,7 +214,7 @@
}
};
return ctrl;
angular.extend(this, ctrl);
}],
link: function (scope, elm, attrs, bindonceController)
......@@ -318,4 +318,4 @@
return bindonceDirective;
});
})
})();
\ No newline at end of file
})();
......@@ -59,7 +59,6 @@ module.exports = function(config,grunt) {
'services/all',
'angular-strap',
'directives/all',
'jquery.flot.pie',
'angular-dragdrop',
'controllers/all',
'routes/all',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment