mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'panelbase'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ awsconfig
|
||||
/dist
|
||||
/emails/dist
|
||||
/public_gen
|
||||
/public/vendor/npm
|
||||
/tmp
|
||||
vendor/phantomjs/phantomjs
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"coveralls": "grunt karma:coveralls && rm -rf ./coverage"
|
||||
"coveralls": "grunt karma:coveralls && rm -rf ./coverage",
|
||||
"postinstall": "grunt copy:node_modules"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -123,6 +123,8 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||
panels[panel.Id] = map[string]interface{}{
|
||||
"module": panel.Module,
|
||||
"name": panel.Name,
|
||||
"id": panel.Id,
|
||||
"info": panel.Info,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import store from 'app/core/store';
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import coreModule from '../core_module';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
export class GrafanaCtrl {
|
||||
|
||||
|
||||
@@ -59,11 +59,14 @@ coreModule.filter('noXml', function() {
|
||||
|
||||
coreModule.filter('interpolateTemplateVars', function (templateSrv) {
|
||||
var filterFunc: any = function(text, scope) {
|
||||
if (scope.panel) {
|
||||
return templateSrv.replaceWithText(text, scope.panel.scopedVars);
|
||||
var scopedVars;
|
||||
if (scope.ctrl && scope.ctrl.panel) {
|
||||
scopedVars = scope.ctrl.panel.scopedVars;
|
||||
} else {
|
||||
return templateSrv.replaceWithText(text, scope.row.scopedVars);
|
||||
scopedVars = scope.row.scopedVars;
|
||||
}
|
||||
|
||||
return templateSrv.replaceWithText(text, scopedVars);
|
||||
};
|
||||
|
||||
filterFunc.$stateful = true;
|
||||
|
||||
@@ -116,36 +116,7 @@ function (angular, _, config) {
|
||||
$scope.$broadcast('render');
|
||||
};
|
||||
|
||||
$scope.removePanel = function(panel) {
|
||||
$scope.appEvent('confirm-modal', {
|
||||
title: 'Are you sure you want to remove this panel?',
|
||||
icon: 'fa-trash',
|
||||
yesText: 'Delete',
|
||||
onConfirm: function() {
|
||||
$scope.row.panels = _.without($scope.row.panels, panel);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updatePanelSpan = function(panel, span) {
|
||||
panel.span = Math.min(Math.max(Math.floor(panel.span + span), 1), 12);
|
||||
};
|
||||
|
||||
$scope.replacePanel = function(newPanel, oldPanel) {
|
||||
var row = $scope.row;
|
||||
var index = _.indexOf(row.panels, oldPanel);
|
||||
row.panels.splice(index, 1);
|
||||
|
||||
// adding it back needs to be done in next digest
|
||||
$timeout(function() {
|
||||
newPanel.id = oldPanel.id;
|
||||
newPanel.span = oldPanel.span;
|
||||
row.panels.splice(index, 0, newPanel);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
|
||||
module.directive('rowHeight', function() {
|
||||
|
||||
@@ -26,7 +26,7 @@ function (angular, _, require, config) {
|
||||
$scope.modalTitle = 'Share Dashboard';
|
||||
}
|
||||
|
||||
if (!$scope.dashboardMeta.isSnapshot) {
|
||||
if (!$scope.dashboard.meta.isSnapshot) {
|
||||
$scope.tabs.push({title: 'Snapshot sharing', src: 'shareSnapshot.html'});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ function (angular, _, $) {
|
||||
self.state = {};
|
||||
self.panelScopes = [];
|
||||
self.$scope = $scope;
|
||||
self.dashboard = $scope.dashboard;
|
||||
|
||||
$scope.exitFullscreen = function() {
|
||||
if (self.state.fullscreen) {
|
||||
@@ -31,6 +32,14 @@ function (angular, _, $) {
|
||||
}
|
||||
});
|
||||
|
||||
$scope.onAppEvent('panel-change-view', function(evt, payload) {
|
||||
self.update(payload);
|
||||
});
|
||||
|
||||
$scope.onAppEvent('panel-instantiated', function(evt, payload) {
|
||||
self.registerPanel(payload.scope);
|
||||
});
|
||||
|
||||
this.update(this.getQueryStringState(), true);
|
||||
this.expandRowForPanel();
|
||||
}
|
||||
@@ -66,7 +75,7 @@ function (angular, _, $) {
|
||||
|
||||
DashboardViewState.prototype.update = function(state, skipUrlSync) {
|
||||
_.extend(this.state, state);
|
||||
this.fullscreen = this.state.fullscreen;
|
||||
this.dashboard.meta.fullscreen = this.state.fullscreen;
|
||||
|
||||
if (!this.state.fullscreen) {
|
||||
this.state.panelId = null;
|
||||
@@ -84,7 +93,7 @@ function (angular, _, $) {
|
||||
DashboardViewState.prototype.syncState = function() {
|
||||
if (this.panelScopes.length === 0) { return; }
|
||||
|
||||
if (this.fullscreen) {
|
||||
if (this.dashboard.meta.fullscreen) {
|
||||
if (this.fullscreenPanel) {
|
||||
this.leaveFullscreen(false);
|
||||
}
|
||||
@@ -105,23 +114,24 @@ function (angular, _, $) {
|
||||
|
||||
DashboardViewState.prototype.getPanelScope = function(id) {
|
||||
return _.find(this.panelScopes, function(panelScope) {
|
||||
return panelScope.panel.id === id;
|
||||
return panelScope.ctrl.panel.id === id;
|
||||
});
|
||||
};
|
||||
|
||||
DashboardViewState.prototype.leaveFullscreen = function(render) {
|
||||
var self = this;
|
||||
var ctrl = self.fullscreenPanel.ctrl;
|
||||
|
||||
self.fullscreenPanel.editMode = false;
|
||||
self.fullscreenPanel.fullscreen = false;
|
||||
delete self.fullscreenPanel.height;
|
||||
ctrl.editMode = false;
|
||||
ctrl.fullscreen = false;
|
||||
delete ctrl.height;
|
||||
|
||||
this.$scope.appEvent('panel-fullscreen-exit', {panelId: this.fullscreenPanel.panel.id});
|
||||
this.$scope.appEvent('panel-fullscreen-exit', {panelId: ctrl.panel.id});
|
||||
|
||||
if (!render) { return false;}
|
||||
|
||||
$timeout(function() {
|
||||
if (self.oldTimeRange !== self.fullscreenPanel.range) {
|
||||
if (self.oldTimeRange !== ctrl.range) {
|
||||
self.$scope.broadcastRefresh();
|
||||
}
|
||||
else {
|
||||
@@ -135,17 +145,18 @@ function (angular, _, $) {
|
||||
var docHeight = $(window).height();
|
||||
var editHeight = Math.floor(docHeight * 0.3);
|
||||
var fullscreenHeight = Math.floor(docHeight * 0.7);
|
||||
var ctrl = panelScope.ctrl;
|
||||
|
||||
panelScope.editMode = this.state.edit && this.$scope.dashboardMeta.canEdit;
|
||||
panelScope.height = panelScope.editMode ? editHeight : fullscreenHeight;
|
||||
ctrl.editMode = this.state.edit && this.$scope.dashboardMeta.canEdit;
|
||||
ctrl.height = ctrl.editMode ? editHeight : fullscreenHeight;
|
||||
ctrl.fullscreen = true;
|
||||
|
||||
this.oldTimeRange = panelScope.range;
|
||||
this.oldTimeRange = ctrl.range;
|
||||
this.fullscreenPanel = panelScope;
|
||||
|
||||
$(window).scrollTop(0);
|
||||
|
||||
panelScope.fullscreen = true;
|
||||
this.$scope.appEvent('panel-fullscreen-enter', {panelId: panelScope.panel.id});
|
||||
this.$scope.appEvent('panel-fullscreen-enter', {panelId: ctrl.panel.id});
|
||||
|
||||
$timeout(function() {
|
||||
panelScope.$broadcast('render');
|
||||
@@ -156,8 +167,12 @@ function (angular, _, $) {
|
||||
var self = this;
|
||||
self.panelScopes.push(panelScope);
|
||||
|
||||
if (self.state.panelId === panelScope.panel.id) {
|
||||
self.enterFullscreen(panelScope);
|
||||
if (self.state.panelId === panelScope.ctrl.panel.id) {
|
||||
if (self.state.edit) {
|
||||
panelScope.ctrl.editPanel();
|
||||
} else {
|
||||
panelScope.ctrl.viewPanel();
|
||||
}
|
||||
}
|
||||
|
||||
panelScope.$on('$destroy', function() {
|
||||
|
||||
@@ -6,4 +6,5 @@ define([
|
||||
'./solo_panel_ctrl',
|
||||
'./panel_loader',
|
||||
'./query_editor',
|
||||
'./panel_editor_tab',
|
||||
], function () {});
|
||||
|
||||
234
public/app/features/panel/metrics_panel_ctrl.ts
Normal file
234
public/app/features/panel/metrics_panel_ctrl.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {PanelCtrl} from './panel_ctrl';
|
||||
|
||||
import * as rangeUtil from '../../core/utils/rangeutil';
|
||||
import * as dateMath from '../../core/utils/datemath';
|
||||
|
||||
class MetricsPanelCtrl extends PanelCtrl {
|
||||
error: boolean;
|
||||
loading: boolean;
|
||||
datasource: any;
|
||||
$q: any;
|
||||
$timeout: any;
|
||||
datasourceSrv: any;
|
||||
timeSrv: any;
|
||||
timing: any;
|
||||
range: any;
|
||||
rangeRaw: any;
|
||||
interval: any;
|
||||
resolution: any;
|
||||
timeInfo: any;
|
||||
skipDataOnInit: boolean;
|
||||
datasources: any[];
|
||||
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
|
||||
// make metrics tab the default
|
||||
this.editorTabIndex = 1;
|
||||
this.$q = $injector.get('$q');
|
||||
this.datasourceSrv = $injector.get('datasourceSrv');
|
||||
this.timeSrv = $injector.get('timeSrv');
|
||||
|
||||
if (!this.panel.targets) {
|
||||
this.panel.targets = [{}];
|
||||
}
|
||||
|
||||
// hookup initial data fetch
|
||||
this.$timeout(() => {
|
||||
if (!this.skipDataOnInit) {
|
||||
this.refresh();
|
||||
}
|
||||
}, 30);;
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
this.addEditorTab('Metrics', 'public/app/partials/metrics.html');
|
||||
this.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||
this.datasources = this.datasourceSrv.getMetricSources();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
refreshData(data) {
|
||||
// null op
|
||||
return this.$q.when(data);
|
||||
}
|
||||
|
||||
loadSnapshot(data) {
|
||||
// null op
|
||||
return data;
|
||||
}
|
||||
|
||||
getData() {
|
||||
// ignore fetching data if another panel is in fullscreen
|
||||
if (this.otherPanelInFullscreenMode()) { return; }
|
||||
|
||||
// if we have snapshot data use that
|
||||
if (this.panel.snapshotData) {
|
||||
if (this.loadSnapshot) {
|
||||
this.loadSnapshot(this.panel.snapshotData);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// clear loading/error state
|
||||
delete this.error;
|
||||
this.loading = true;
|
||||
|
||||
// load datasource service
|
||||
this.datasourceSrv.get(this.panel.datasource).then(datasource => {
|
||||
this.datasource = datasource;
|
||||
return this.refreshData(this.datasource);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
}).catch(err => {
|
||||
console.log('Panel data error:', err);
|
||||
this.loading = false;
|
||||
this.error = err.message || "Timeseries data request error";
|
||||
this.inspector = {error: err};
|
||||
});
|
||||
}
|
||||
|
||||
setTimeQueryStart() {
|
||||
this.timing = {};
|
||||
this.timing.queryStart = new Date().getTime();
|
||||
}
|
||||
|
||||
setTimeQueryEnd() {
|
||||
this.timing.queryEnd = new Date().getTime();
|
||||
}
|
||||
|
||||
updateTimeRange() {
|
||||
this.range = this.timeSrv.timeRange();
|
||||
this.rangeRaw = this.timeSrv.timeRange(false);
|
||||
|
||||
this.applyPanelTimeOverrides();
|
||||
|
||||
if (this.panel.maxDataPoints) {
|
||||
this.resolution = this.panel.maxDataPoints;
|
||||
} else {
|
||||
this.resolution = Math.ceil($(window).width() * (this.panel.span / 12));
|
||||
}
|
||||
|
||||
var panelInterval = this.panel.interval;
|
||||
var datasourceInterval = (this.datasource || {}).interval;
|
||||
this.interval = kbn.calculateInterval(this.range, this.resolution, panelInterval || datasourceInterval);
|
||||
};
|
||||
|
||||
applyPanelTimeOverrides() {
|
||||
this.timeInfo = '';
|
||||
|
||||
// check panel time overrrides
|
||||
if (this.panel.timeFrom) {
|
||||
var timeFromInfo = rangeUtil.describeTextRange(this.panel.timeFrom);
|
||||
if (timeFromInfo.invalid) {
|
||||
this.timeInfo = 'invalid time override';
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isString(this.rangeRaw.from)) {
|
||||
var timeFromDate = dateMath.parse(timeFromInfo.from);
|
||||
this.timeInfo = timeFromInfo.display;
|
||||
this.rangeRaw.from = timeFromInfo.from;
|
||||
this.rangeRaw.to = timeFromInfo.to;
|
||||
this.range.from = timeFromDate;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.panel.timeShift) {
|
||||
var timeShiftInfo = rangeUtil.describeTextRange(this.panel.timeShift);
|
||||
if (timeShiftInfo.invalid) {
|
||||
this.timeInfo = 'invalid timeshift';
|
||||
return;
|
||||
}
|
||||
|
||||
var timeShift = '-' + this.panel.timeShift;
|
||||
this.timeInfo += ' timeshift ' + timeShift;
|
||||
this.range.from = dateMath.parseDateMath(timeShift, this.range.from, false);
|
||||
this.range.to = dateMath.parseDateMath(timeShift, this.range.to, true);
|
||||
|
||||
this.rangeRaw = this.range;
|
||||
}
|
||||
|
||||
if (this.panel.hideTimeOverride) {
|
||||
this.timeInfo = '';
|
||||
}
|
||||
};
|
||||
|
||||
issueQueries(datasource) {
|
||||
if (!this.panel.targets || this.panel.targets.length === 0) {
|
||||
return this.$q.when([]);
|
||||
}
|
||||
|
||||
this.updateTimeRange();
|
||||
|
||||
var metricsQuery = {
|
||||
range: this.range,
|
||||
rangeRaw: this.rangeRaw,
|
||||
interval: this.interval,
|
||||
targets: this.panel.targets,
|
||||
format: this.panel.renderer === 'png' ? 'png' : 'json',
|
||||
maxDataPoints: this.resolution,
|
||||
scopedVars: this.panel.scopedVars,
|
||||
cacheTimeout: this.panel.cacheTimeout
|
||||
};
|
||||
|
||||
this.setTimeQueryStart();
|
||||
return datasource.query(metricsQuery).then(results => {
|
||||
this.setTimeQueryEnd();
|
||||
|
||||
if (this.dashboard.snapshot) {
|
||||
this.panel.snapshotData = results;
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
addDataQuery(datasource) {
|
||||
this.dashboard.addDataQueryTo(this.panel, datasource);
|
||||
}
|
||||
|
||||
removeDataQuery(query) {
|
||||
this.dashboard.removeDataQuery(this.panel, query);
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
duplicateDataQuery(query) {
|
||||
this.dashboard.duplicateDataQuery(this.panel, query);
|
||||
}
|
||||
|
||||
moveDataQuery(fromIndex, toIndex) {
|
||||
this.dashboard.moveDataQuery(this.panel, fromIndex, toIndex);
|
||||
}
|
||||
|
||||
setDatasource(datasource) {
|
||||
// switching to mixed
|
||||
if (datasource.meta.mixed) {
|
||||
_.each(this.panel.targets, target => {
|
||||
target.datasource = this.panel.datasource;
|
||||
if (target.datasource === null) {
|
||||
target.datasource = config.defaultDatasource;
|
||||
}
|
||||
});
|
||||
} else if (this.datasource && this.datasource.meta.mixed) {
|
||||
_.each(this.panel.targets, target => {
|
||||
delete target.datasource;
|
||||
});
|
||||
}
|
||||
|
||||
this.panel.datasource = datasource.value;
|
||||
this.datasource = null;
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
export {MetricsPanelCtrl};
|
||||
50
public/app/features/panel/panel.ts
Normal file
50
public/app/features/panel/panel.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
import {PanelCtrl} from './panel_ctrl';
|
||||
import {MetricsPanelCtrl} from './metrics_panel_ctrl';
|
||||
|
||||
export class DefaultPanelCtrl extends PanelCtrl {
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
}
|
||||
}
|
||||
|
||||
class PanelDirective {
|
||||
template: string;
|
||||
templateUrl: string;
|
||||
bindToController: boolean;
|
||||
scope: any;
|
||||
controller: any;
|
||||
controllerAs: string;
|
||||
|
||||
getDirective() {
|
||||
if (!this.controller) {
|
||||
this.controller = DefaultPanelCtrl;
|
||||
}
|
||||
|
||||
return {
|
||||
template: this.template,
|
||||
templateUrl: this.templateUrl,
|
||||
controller: this.controller,
|
||||
controllerAs: 'ctrl',
|
||||
bindToController: true,
|
||||
scope: {dashboard: "=", panel: "=", row: "="},
|
||||
link: (scope, elem, attrs, ctrl) => {
|
||||
ctrl.init();
|
||||
this.link(scope, elem, attrs, ctrl);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
PanelCtrl,
|
||||
MetricsPanelCtrl,
|
||||
PanelDirective,
|
||||
}
|
||||
179
public/app/features/panel/panel_ctrl.ts
Normal file
179
public/app/features/panel/panel_ctrl.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class PanelCtrl {
|
||||
panel: any;
|
||||
row: any;
|
||||
dashboard: any;
|
||||
editorTabIndex: number;
|
||||
pluginName: string;
|
||||
pluginId: string;
|
||||
icon: string;
|
||||
editorTabs: any;
|
||||
$scope: any;
|
||||
$injector: any;
|
||||
$timeout: any;
|
||||
fullscreen: boolean;
|
||||
inspector: any;
|
||||
editModeInitiated: boolean;
|
||||
editorHelpIndex: number;
|
||||
|
||||
constructor($scope, $injector) {
|
||||
this.$injector = $injector;
|
||||
this.$scope = $scope;
|
||||
this.$timeout = $injector.get('$timeout');
|
||||
this.editorTabIndex = 0;
|
||||
|
||||
var plugin = config.panels[this.panel.type];
|
||||
if (plugin) {
|
||||
this.pluginId = plugin.id;
|
||||
this.pluginName = plugin.name;
|
||||
}
|
||||
|
||||
$scope.$on("refresh", () => this.refresh());
|
||||
}
|
||||
|
||||
init() {
|
||||
this.publishAppEvent('panel-instantiated', {scope: this.$scope});
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
renderingCompleted() {
|
||||
this.$scope.$root.performance.panelsRendered++;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
return;
|
||||
}
|
||||
|
||||
publishAppEvent(evtName, evt) {
|
||||
this.$scope.$root.appEvent(evtName, evt);
|
||||
}
|
||||
|
||||
changeView(fullscreen, edit) {
|
||||
this.publishAppEvent('panel-change-view', {
|
||||
fullscreen: fullscreen, edit: edit, panelId: this.panel.id
|
||||
});
|
||||
}
|
||||
|
||||
viewPanel() {
|
||||
this.changeView(true, false);
|
||||
}
|
||||
|
||||
editPanel() {
|
||||
if (!this.editModeInitiated) {
|
||||
this.editorTabs = [];
|
||||
this.addEditorTab('General', 'public/app/partials/panelgeneral.html');
|
||||
this.initEditMode();
|
||||
}
|
||||
|
||||
this.changeView(true, true);
|
||||
}
|
||||
|
||||
exitFullscreen() {
|
||||
this.changeView(false, false);
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
return;
|
||||
}
|
||||
|
||||
addEditorTab(title, directiveFn, index?) {
|
||||
var editorTab = {title, directiveFn};
|
||||
|
||||
if (_.isString(directiveFn)) {
|
||||
editorTab.directiveFn = function() {
|
||||
return {templateUrl: directiveFn};
|
||||
};
|
||||
}
|
||||
if (index) {
|
||||
this.editorTabs.splice(index, 0, editorTab);
|
||||
} else {
|
||||
this.editorTabs.push(editorTab);
|
||||
}
|
||||
}
|
||||
|
||||
getMenu() {
|
||||
let menu = [];
|
||||
menu.push({text: 'View', click: 'ctrl.viewPanel(); dismiss();'});
|
||||
menu.push({text: 'Edit', click: 'ctrl.editPanel(); dismiss();', role: 'Editor'});
|
||||
menu.push({text: 'Duplicate', click: 'ctrl.duplicate()', role: 'Editor' });
|
||||
menu.push({text: 'Share', click: 'ctrl.sharePanel(); dismiss();'});
|
||||
return menu;
|
||||
}
|
||||
|
||||
getExtendedMenu() {
|
||||
return [{text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();'}];
|
||||
}
|
||||
|
||||
otherPanelInFullscreenMode() {
|
||||
return this.dashboard.meta.fullscreen && !this.fullscreen;
|
||||
}
|
||||
|
||||
broadcastRender(arg1?, arg2?) {
|
||||
this.$scope.$broadcast('render', arg1, arg2);
|
||||
}
|
||||
|
||||
toggleEditorHelp(index) {
|
||||
if (this.editorHelpIndex === index) {
|
||||
this.editorHelpIndex = null;
|
||||
return;
|
||||
}
|
||||
this.editorHelpIndex = index;
|
||||
}
|
||||
|
||||
duplicate() {
|
||||
this.dashboard.duplicatePanel(this.panel, this.row);
|
||||
}
|
||||
|
||||
updateColumnSpan(span) {
|
||||
this.panel.span = Math.min(Math.max(Math.floor(this.panel.span + span), 1), 12);
|
||||
this.$timeout(() => {
|
||||
this.broadcastRender();
|
||||
});
|
||||
}
|
||||
|
||||
removePanel() {
|
||||
this.publishAppEvent('confirm-modal', {
|
||||
title: 'Are you sure you want to remove this panel?',
|
||||
icon: 'fa-trash',
|
||||
yesText: 'Delete',
|
||||
onConfirm: () => {
|
||||
this.row.panels = _.without(this.row.panels, this.panel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editPanelJson() {
|
||||
this.publishAppEvent('show-json-editor', {
|
||||
object: this.panel,
|
||||
updateHandler: this.replacePanel.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
replacePanel(newPanel, oldPanel) {
|
||||
var row = this.row;
|
||||
var index = _.indexOf(this.row.panels, oldPanel);
|
||||
this.row.panels.splice(index, 1);
|
||||
|
||||
// adding it back needs to be done in next digest
|
||||
this.$timeout(() => {
|
||||
newPanel.id = oldPanel.id;
|
||||
newPanel.span = oldPanel.span;
|
||||
this.row.panels.splice(index, 0, newPanel);
|
||||
});
|
||||
}
|
||||
|
||||
sharePanel() {
|
||||
var shareScope = this.$scope.$new();
|
||||
shareScope.panel = this.panel;
|
||||
shareScope.dashboard = this.dashboard;
|
||||
|
||||
this.publishAppEvent('show-modal', {
|
||||
src: './app/features/dashboard/partials/shareModal.html',
|
||||
scope: shareScope
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,13 @@ function (angular, $) {
|
||||
restrict: 'E',
|
||||
templateUrl: 'app/features/panel/partials/panel.html',
|
||||
transclude: true,
|
||||
scope: { ctrl: "=" },
|
||||
link: function(scope, elem) {
|
||||
var panelContainer = elem.find('.panel-container');
|
||||
|
||||
scope.$watchGroup(['fullscreen', 'height', 'panel.height', 'row.height'], function() {
|
||||
panelContainer.css({ minHeight: scope.height || scope.panel.height || scope.row.height, display: 'block' });
|
||||
elem.toggleClass('panel-fullscreen', scope.fullscreen ? true : false);
|
||||
var ctrl = scope.ctrl;
|
||||
scope.$watchGroup(['ctrl.fullscreen', 'ctrl.height', 'ctrl.panel.height', 'ctrl.row.height'], function() {
|
||||
panelContainer.css({ minHeight: ctrl.height || ctrl.panel.height || ctrl.row.height, display: 'block' });
|
||||
elem.toggleClass('panel-fullscreen', ctrl.fullscreen ? true : false);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -30,6 +31,7 @@ function (angular, $) {
|
||||
link: function(scope, elem) {
|
||||
var resizing = false;
|
||||
var lastPanel = false;
|
||||
var ctrl = scope.ctrl;
|
||||
var handleOffset;
|
||||
var originalHeight;
|
||||
var originalWidth;
|
||||
@@ -40,31 +42,31 @@ function (angular, $) {
|
||||
resizing = true;
|
||||
|
||||
handleOffset = $(e.target).offset();
|
||||
originalHeight = parseInt(scope.row.height);
|
||||
originalWidth = scope.panel.span;
|
||||
originalHeight = parseInt(ctrl.row.height);
|
||||
originalWidth = ctrl.panel.span;
|
||||
maxWidth = $(document).width();
|
||||
|
||||
lastPanel = scope.row.panels[scope.row.panels.length - 1];
|
||||
lastPanel = ctrl.row.panels[ctrl.row.panels.length - 1];
|
||||
|
||||
$('body').on('mousemove', moveHandler);
|
||||
$('body').on('mouseup', dragEndHandler);
|
||||
}
|
||||
|
||||
function moveHandler(e) {
|
||||
scope.row.height = originalHeight + (e.pageY - handleOffset.top);
|
||||
scope.panel.span = originalWidth + (((e.pageX - handleOffset.left) / maxWidth) * 12);
|
||||
scope.panel.span = Math.min(Math.max(scope.panel.span, 1), 12);
|
||||
ctrl.row.height = originalHeight + (e.pageY - handleOffset.top);
|
||||
ctrl.panel.span = originalWidth + (((e.pageX - handleOffset.left) / maxWidth) * 12);
|
||||
ctrl.panel.span = Math.min(Math.max(ctrl.panel.span, 1), 12);
|
||||
|
||||
var rowSpan = scope.dashboard.rowSpan(scope.row);
|
||||
var rowSpan = ctrl.dashboard.rowSpan(ctrl.row);
|
||||
|
||||
// auto adjust other panels
|
||||
if (Math.floor(rowSpan) < 14) {
|
||||
// last panel should not push row down
|
||||
if (lastPanel === scope.panel && rowSpan > 12) {
|
||||
if (lastPanel === ctrl.panel && rowSpan > 12) {
|
||||
lastPanel.span -= rowSpan - 12;
|
||||
}
|
||||
// reduce width of last panel so total in row is 12
|
||||
else if (lastPanel !== scope.panel) {
|
||||
else if (lastPanel !== ctrl.panel) {
|
||||
lastPanel.span = lastPanel.span - (rowSpan - 12);
|
||||
lastPanel.span = Math.min(Math.max(lastPanel.span, 1), 12);
|
||||
}
|
||||
@@ -77,7 +79,7 @@ function (angular, $) {
|
||||
|
||||
function dragEndHandler() {
|
||||
// if close to 12
|
||||
var rowSpan = scope.dashboard.rowSpan(scope.row);
|
||||
var rowSpan = ctrl.dashboard.rowSpan(ctrl.row);
|
||||
if (rowSpan < 12 && rowSpan > 11) {
|
||||
lastPanel.span += 12 - rowSpan;
|
||||
}
|
||||
|
||||
28
public/app/features/panel/panel_editor_tab.ts
Normal file
28
public/app/features/panel/panel_editor_tab.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import config from 'app/core/config';
|
||||
|
||||
var directiveModule = angular.module('grafana.directives');
|
||||
|
||||
/** @ngInject */
|
||||
function panelEditorTab(dynamicDirectiveSrv) {
|
||||
return dynamicDirectiveSrv.create({
|
||||
scope: {
|
||||
ctrl: "=",
|
||||
editorTab: "=",
|
||||
index: "=",
|
||||
},
|
||||
directive: scope => {
|
||||
var pluginId = scope.ctrl.pluginId;
|
||||
var tabIndex = scope.index;
|
||||
|
||||
return Promise.resolve({
|
||||
name: `panel-editor-tab-${pluginId}${tabIndex}`,
|
||||
fn: scope.editorTab.directiveFn,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
directiveModule.directive('panelEditorTab', panelEditorTab);
|
||||
@@ -3,28 +3,82 @@
|
||||
import angular from 'angular';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import {unknownPanelDirective} from '../../plugins/panel/unknown/module';
|
||||
import {UnknownPanel} from '../../plugins/panel/unknown/module';
|
||||
|
||||
var directiveModule = angular.module('grafana.directives');
|
||||
|
||||
/** @ngInject */
|
||||
function panelLoader($parse, dynamicDirectiveSrv) {
|
||||
return dynamicDirectiveSrv.create({
|
||||
directive: scope => {
|
||||
let panelInfo = config.panels[scope.panel.type];
|
||||
if (!panelInfo) {
|
||||
return Promise.resolve({
|
||||
name: 'panel-directive-' + scope.panel.type,
|
||||
fn: unknownPanelDirective
|
||||
function panelLoader($compile, dynamicDirectiveSrv, $http, $q, $injector) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
dashboard: "=",
|
||||
row: "=",
|
||||
panel: "="
|
||||
},
|
||||
link: function(scope, elem, attrs) {
|
||||
|
||||
function getTemplate(directive) {
|
||||
if (directive.template) {
|
||||
return $q.when(directive.template);
|
||||
}
|
||||
return $http.get(directive.templateUrl).then(res => {
|
||||
return res.data;
|
||||
});
|
||||
}
|
||||
|
||||
return System.import(panelInfo.module).then(function(panelModule) {
|
||||
return {
|
||||
name: 'panel-directive-' + scope.panel.type,
|
||||
fn: panelModule.panel,
|
||||
};
|
||||
function addPanelAndCompile(name) {
|
||||
var child = angular.element(document.createElement(name));
|
||||
child.attr('dashboard', 'dashboard');
|
||||
child.attr('panel', 'panel');
|
||||
child.attr('row', 'row');
|
||||
$compile(child)(scope);
|
||||
|
||||
elem.empty();
|
||||
elem.append(child);
|
||||
}
|
||||
|
||||
function addPanel(name, Panel) {
|
||||
if (Panel.registered) {
|
||||
addPanelAndCompile(name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Panel.promise) {
|
||||
Panel.promise.then(() => {
|
||||
addPanelAndCompile(name);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var panelInstance = $injector.instantiate(Panel);
|
||||
var directive = panelInstance.getDirective();
|
||||
|
||||
Panel.promise = getTemplate(directive).then(template => {
|
||||
directive.templateUrl = null;
|
||||
directive.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
|
||||
directiveModule.directive(attrs.$normalize(name), function() {
|
||||
return directive;
|
||||
});
|
||||
Panel.registered = true;
|
||||
addPanelAndCompile(name);
|
||||
});
|
||||
}
|
||||
|
||||
var panelElemName = 'panel-directive-' + scope.panel.type;
|
||||
let panelInfo = config.panels[scope.panel.type];
|
||||
if (!panelInfo) {
|
||||
addPanel(panelElemName, UnknownPanel);
|
||||
return;
|
||||
}
|
||||
|
||||
System.import(panelInfo.module).then(function(panelModule) {
|
||||
addPanel(panelElemName, panelModule.Panel);
|
||||
}).catch(err => {
|
||||
console.log('Panel err: ', err);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('grafana.directives').directive('panelLoader', panelLoader);
|
||||
directiveModule.directive('panelLoader', panelLoader);
|
||||
|
||||
@@ -11,32 +11,32 @@ function (angular, $, _) {
|
||||
.directive('panelMenu', function($compile, linkSrv) {
|
||||
var linkTemplate =
|
||||
'<span class="panel-title drag-handle pointer">' +
|
||||
'<span class="panel-title-text drag-handle">{{panel.title | interpolateTemplateVars:this}}</span>' +
|
||||
'<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>' +
|
||||
'<span class="panel-time-info" ng-show="panelMeta.timeInfo"><i class="fa fa-clock-o"></i> {{panelMeta.timeInfo}}</span>' +
|
||||
'<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>' +
|
||||
'<span class="panel-links-btn"><i class="fa fa-link"></i></span>' +
|
||||
'<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>' +
|
||||
'</span>';
|
||||
|
||||
function createExternalLinkMenu($scope) {
|
||||
function createExternalLinkMenu(ctrl) {
|
||||
var template = '<div class="panel-menu small">';
|
||||
template += '<div class="panel-menu-row">';
|
||||
|
||||
if ($scope.panel.links) {
|
||||
_.each($scope.panel.links, function(link) {
|
||||
var info = linkSrv.getPanelLinkAnchorInfo(link, $scope.panel.scopedVars);
|
||||
if (ctrl.panel.links) {
|
||||
_.each(ctrl.panel.links, function(link) {
|
||||
var info = linkSrv.getPanelLinkAnchorInfo(link, ctrl.panel.scopedVars);
|
||||
template += '<a class="panel-menu-link" href="' + info.href + '" target="' + info.target + '">' + info.title + '</a>';
|
||||
});
|
||||
}
|
||||
return template;
|
||||
}
|
||||
function createMenuTemplate($scope) {
|
||||
function createMenuTemplate(ctrl) {
|
||||
var template = '<div class="panel-menu small">';
|
||||
|
||||
if ($scope.dashboardMeta.canEdit) {
|
||||
if (ctrl.dashboard.meta.canEdit) {
|
||||
template += '<div class="panel-menu-inner">';
|
||||
template += '<div class="panel-menu-row">';
|
||||
template += '<a class="panel-menu-icon pull-left" ng-click="updateColumnSpan(-1)"><i class="fa fa-minus"></i></a>';
|
||||
template += '<a class="panel-menu-icon pull-left" ng-click="updateColumnSpan(1)"><i class="fa fa-plus"></i></a>';
|
||||
template += '<a class="panel-menu-icon pull-right" ng-click="removePanel(panel)"><i class="fa fa-remove"></i></a>';
|
||||
template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(-1)"><i class="fa fa-minus"></i></a>';
|
||||
template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(1)"><i class="fa fa-plus"></i></a>';
|
||||
template += '<a class="panel-menu-icon pull-right" ng-click="ctrl.removePanel()"><i class="fa fa-remove"></i></a>';
|
||||
template += '<div class="clearfix"></div>';
|
||||
template += '</div>';
|
||||
}
|
||||
@@ -44,9 +44,9 @@ function (angular, $, _) {
|
||||
template += '<div class="panel-menu-row">';
|
||||
template += '<a class="panel-menu-link" gf-dropdown="extendedMenu"><i class="fa fa-bars"></i></a>';
|
||||
|
||||
_.each($scope.panelMeta.menu, function(item) {
|
||||
_.each(ctrl.getMenu(), function(item) {
|
||||
// skip edit actions if not editor
|
||||
if (item.role === 'Editor' && !$scope.dashboardMeta.canEdit) {
|
||||
if (item.role === 'Editor' && !ctrl.dashboard.meta.canEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ function (angular, $, _) {
|
||||
return template;
|
||||
}
|
||||
|
||||
function getExtendedMenu($scope) {
|
||||
return angular.copy($scope.panelMeta.extendedMenu);
|
||||
function getExtendedMenu(ctrl) {
|
||||
return ctrl.getExtendedMenu();
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -74,13 +74,14 @@ function (angular, $, _) {
|
||||
var $panelLinksBtn = $link.find(".panel-links-btn");
|
||||
var $panelContainer = elem.parents(".panel-container");
|
||||
var menuScope = null;
|
||||
var ctrl = $scope.ctrl;
|
||||
var timeout = null;
|
||||
var $menu = null;
|
||||
|
||||
elem.append($link);
|
||||
|
||||
$scope.$watchCollection('panel.links', function(newValue) {
|
||||
var showIcon = (newValue ? newValue.length > 0 : false) && $scope.panel.title !== '';
|
||||
$scope.$watchCollection('ctrl.panel.links', function(newValue) {
|
||||
var showIcon = (newValue ? newValue.length > 0 : false) && ctrl.panel.title !== '';
|
||||
$panelLinksBtn.toggle(showIcon);
|
||||
});
|
||||
|
||||
@@ -95,7 +96,7 @@ function (angular, $, _) {
|
||||
|
||||
// if hovering or draging pospone close
|
||||
if (force !== true) {
|
||||
if ($menu.is(':hover') || $scope.dashboard.$$panelDragging) {
|
||||
if ($menu.is(':hover') || $scope.ctrl.dashboard.$$panelDragging) {
|
||||
dismiss(2200);
|
||||
return;
|
||||
}
|
||||
@@ -126,7 +127,7 @@ function (angular, $, _) {
|
||||
if ($(e.target).hasClass('fa-link')) {
|
||||
menuTemplate = createExternalLinkMenu($scope);
|
||||
} else {
|
||||
menuTemplate = createMenuTemplate($scope);
|
||||
menuTemplate = createMenuTemplate(ctrl);
|
||||
}
|
||||
|
||||
$menu = $(menuTemplate);
|
||||
@@ -135,7 +136,7 @@ function (angular, $, _) {
|
||||
});
|
||||
|
||||
menuScope = $scope.$new();
|
||||
menuScope.extendedMenu = getExtendedMenu($scope);
|
||||
menuScope.extendedMenu = getExtendedMenu(ctrl);
|
||||
menuScope.dismiss = function() {
|
||||
dismiss(null, true);
|
||||
};
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
define([
|
||||
'./panel_meta2',
|
||||
],
|
||||
function (panelMeta) {
|
||||
'use strict';
|
||||
return panelMeta.default;
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
export default class PanelMeta {
|
||||
description: any;
|
||||
fullscreen: any;
|
||||
editIcon: any;
|
||||
panelName: any;
|
||||
menu: any;
|
||||
editorTabs: any;
|
||||
extendedMenu: any;
|
||||
|
||||
constructor(options: any) {
|
||||
this.description = options.description;
|
||||
this.fullscreen = options.fullscreen;
|
||||
this.editIcon = options.editIcon;
|
||||
this.panelName = options.panelName;
|
||||
this.menu = [];
|
||||
this.editorTabs = [];
|
||||
this.extendedMenu = [];
|
||||
|
||||
if (options.fullscreen) {
|
||||
this.addMenuItem('View', 'icon-eye-open', 'toggleFullscreen(false); dismiss();');
|
||||
}
|
||||
|
||||
this.addMenuItem('Edit', 'icon-cog', 'editPanel(); dismiss();', 'Editor');
|
||||
this.addMenuItem('Duplicate', 'icon-copy', 'duplicatePanel()', 'Editor');
|
||||
this.addMenuItem('Share', 'icon-share', 'sharePanel(); dismiss();');
|
||||
|
||||
this.addEditorTab('General', 'app/partials/panelgeneral.html');
|
||||
|
||||
if (options.metricsEditor) {
|
||||
this.addEditorTab('Metrics', 'app/partials/metrics.html');
|
||||
}
|
||||
|
||||
this.addExtendedMenuItem('Panel JSON', '', 'editPanelJson(); dismiss();');
|
||||
}
|
||||
|
||||
addMenuItem (text, icon, click, role?) {
|
||||
this.menu.push({text: text, icon: icon, click: click, role: role});
|
||||
}
|
||||
|
||||
addExtendedMenuItem (text, icon, click, role?) {
|
||||
this.extendedMenu.push({text: text, icon: icon, click: click, role: role});
|
||||
}
|
||||
|
||||
addEditorTab (title, src) {
|
||||
this.editorTabs.push({title: title, src: src});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<div class="panel-container" ng-class="{'panel-transparent': panel.transparent}">
|
||||
<div class="panel-container" ng-class="{'panel-transparent': ctrl.panel.transparent}">
|
||||
<div class="panel-header">
|
||||
<span class="alert-error panel-error small pointer" config-modal="app/partials/inspector.html" ng-if="panelMeta.error">
|
||||
<span data-placement="top" bs-tooltip="panelMeta.error">
|
||||
<span class="alert-error panel-error small pointer" config-modal="app/partials/inspector.html" ng-if="ctrl.error">
|
||||
<span data-placement="top" bs-tooltip="ctrl.error">
|
||||
<i class="fa fa-exclamation"></i><span class="panel-error-arrow"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="panel-loading" ng-show="panelMeta.loading">
|
||||
<span class="panel-loading" ng-show="ctrl.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</span>
|
||||
|
||||
@@ -19,27 +19,27 @@
|
||||
<panel-resizer></panel-resizer>
|
||||
</div>
|
||||
|
||||
<div class="panel-full-edit" ng-if="editMode">
|
||||
<div class="panel-full-edit" ng-if="ctrl.editMode">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i ng-class="panelMeta.editIcon"></i>
|
||||
{{panelMeta.panelName}}
|
||||
<i ng-class="ctrl.icon"></i>
|
||||
{{ctrl.name}}
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs>
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
|
||||
<div ng-model="ctrl.editorTabIndex" bs-tabs>
|
||||
<div ng-repeat="tab in ctrl.editorTabs" data-title="{{tab.title}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="exitFullscreen();">
|
||||
<button class="gf-box-header-close-btn" ng-click="ctrl.exitFullscreen();">
|
||||
Back to dashboard
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
|
||||
<div ng-include src="tab.src"></div>
|
||||
<div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
|
||||
<panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input last" placeholder="1h"
|
||||
empty-to-null ng-model="panel.timeFrom" valid-time-span
|
||||
ng-change="get_data()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.timeFrom" valid-time-span
|
||||
ng-change="ctrl.refresh()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -32,8 +32,8 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input last" placeholder="1h"
|
||||
empty-to-null ng-model="panel.timeShift" valid-time-span
|
||||
ng-change="get_data()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.timeShift" valid-time-span
|
||||
ng-change="ctrl.refresh()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -47,9 +47,9 @@
|
||||
<strong>Hide time override info</strong>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<input class="cr1" id="panel.hideTimeOverride" type="checkbox"
|
||||
ng-model="panel.hideTimeOverride" ng-checked="panel.hideTimeOverride" ng-change="get_data()">
|
||||
<label for="panel.hideTimeOverride" class="cr1"></label>
|
||||
<input class="cr1" id="ctrl.panel.hideTimeOverride" type="checkbox"
|
||||
ng-model="ctrl.panel.hideTimeOverride" ng-checked="ctrl.panel.hideTimeOverride" ng-change="ctrl.refresh()">
|
||||
<label for="ctrl.panel.hideTimeOverride" class="cr1"></label>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="panel nospace" ng-if="panel" style="width: 100%">
|
||||
<panel-loader type="panel.type" ng-cloak></panel-loader>
|
||||
<panel-loader dashboard="dashboard" row="row" panel="panel">
|
||||
</panel-loader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,9 +5,9 @@ import angular from 'angular';
|
||||
/** @ngInject */
|
||||
function metricsQueryEditor(dynamicDirectiveSrv, datasourceSrv) {
|
||||
return dynamicDirectiveSrv.create({
|
||||
watchPath: "panel.datasource",
|
||||
watchPath: "ctrl.panel.datasource",
|
||||
directive: scope => {
|
||||
let datasource = scope.target.datasource || scope.panel.datasource;
|
||||
let datasource = scope.target.datasource || scope.ctrl.panel.datasource;
|
||||
return datasourceSrv.get(datasource).then(ds => {
|
||||
scope.datasource = ds;
|
||||
|
||||
@@ -29,9 +29,9 @@ function metricsQueryEditor(dynamicDirectiveSrv, datasourceSrv) {
|
||||
/** @ngInject */
|
||||
function metricsQueryOptions(dynamicDirectiveSrv, datasourceSrv) {
|
||||
return dynamicDirectiveSrv.create({
|
||||
watchPath: "panel.datasource",
|
||||
watchPath: "ctrl.panel.datasource",
|
||||
directive: scope => {
|
||||
return datasourceSrv.get(scope.panel.datasource).then(ds => {
|
||||
return datasourceSrv.get(scope.ctrl.panel.datasource).then(ds => {
|
||||
return System.import(ds.meta.module).then(dsModule => {
|
||||
return {
|
||||
name: 'metrics-query-options-' + ds.meta.id,
|
||||
|
||||
@@ -39,13 +39,8 @@ function (angular, $) {
|
||||
}
|
||||
|
||||
$scope.panel.span = 12;
|
||||
$scope.dashboardViewState = {registerPanel: function() { }, state: {}};
|
||||
};
|
||||
|
||||
if (!$scope.skipAutoInit) {
|
||||
$scope.init();
|
||||
}
|
||||
|
||||
$scope.init();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
4
public/app/headers/common.d.ts
vendored
4
public/app/headers/common.d.ts
vendored
@@ -5,7 +5,7 @@ declare var System: any;
|
||||
|
||||
// dummy modules
|
||||
declare module 'app/core/config' {
|
||||
var config : any;
|
||||
var config: any;
|
||||
export default config;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ declare module 'app/core/utils/kbn' {
|
||||
}
|
||||
|
||||
declare module 'app/core/store' {
|
||||
var store : any;
|
||||
var store: any;
|
||||
export default store;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="row-text pointer" ng-click="toggleRow(row)" ng-bind="row.title | interpolateTemplateVars:this"></div>
|
||||
</div>
|
||||
<div class="row-open" ng-show="!row.collapse">
|
||||
<div class='row-tab bgSuccess dropdown' ng-show="dashboardMeta.canEdit" ng-hide="dashboardViewState.fullscreen">
|
||||
<div class='row-tab bgSuccess dropdown' ng-show="dashboardMeta.canEdit" ng-hide="dashboard.meta.fullscreen">
|
||||
<span class="row-tab-button dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-bars"></i>
|
||||
</span>
|
||||
@@ -79,9 +79,10 @@
|
||||
<div class="row-text pointer" ng-click="toggleRow(row)" ng-if="row.showTitle" ng-bind="row.title | interpolateTemplateVars:this">
|
||||
</div>
|
||||
|
||||
<div ng-repeat="(name, panel) in row.panels track by panel.id" class="panel" ui-draggable="!dashboardViewState.fullscreen" drag="panel.id"
|
||||
ui-on-Drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
|
||||
<panel-loader class="panel-margin"></panel-loader>
|
||||
<div ng-repeat="panel in row.panels track by panel.id" class="panel" ui-draggable="!dashboard.meta.fullscreen" drag="panel.id"
|
||||
ui-on-drop="onDrop($data, row, panel)" drag-handle-class="drag-handle" panel-width>
|
||||
<panel-loader class="panel-margin" dashboard="dashboard" row="row" panel="panel">
|
||||
</panel-loader>
|
||||
</div>
|
||||
|
||||
<div panel-drop-zone class="panel panel-drop-zone" ui-on-drop="onDrop($data, row)" data-drop="true">
|
||||
@@ -97,7 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show='dashboardMeta.canEdit' class="row-fluid add-row-panel-hint" ng-hide="dashboardViewState.fullscreen">
|
||||
<div ng-show='dashboardMeta.canEdit' class="row-fluid add-row-panel-hint" ng-hide="dashboard.meta.fullscreen">
|
||||
<div class="span12" style="text-align:right;">
|
||||
<span style="margin-right: 10px;" ng-click="addRowDefault()" class="pointer btn btn-info btn-small">
|
||||
<span><i class="fa fa-plus"></i> ADD ROW</span>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<div class="editor-row">
|
||||
|
||||
<div class="tight-form-container">
|
||||
<metrics-query-editor ng-repeat="target in panel.targets" ng-class="{'tight-form-disabled': target.hide}" >
|
||||
<metrics-query-editor ng-repeat="target in ctrl.panel.targets" ng-class="{'tight-form-disabled': target.hide}" >
|
||||
</metrics-query-editor>
|
||||
</div>
|
||||
|
||||
<div style="margin: 20px 0 0 0">
|
||||
<button class="btn btn-inverse" ng-click="addDataQuery()" ng-hide="datasource.meta.mixed">
|
||||
<button class="btn btn-inverse" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.datasource.meta.mixed">
|
||||
<i class="fa fa-plus"></i>
|
||||
Query
|
||||
</button>
|
||||
|
||||
<div class="dropdown" ng-if="datasource.meta.mixed">
|
||||
<div class="dropdown" ng-if="ctrl.datasource.meta.mixed">
|
||||
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-plus"></i>
|
||||
Query <span class="caret"></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="datasource in datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
|
||||
<a ng-click="addDataQuery(datasource);">{{datasource.name}}</a>
|
||||
<li ng-repeat="datasource in ctrl.datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
|
||||
<a ng-click="ctrl.addDataQuery(datasource);">{{datasource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -34,12 +34,12 @@
|
||||
<div class="pull-right dropdown" style="margin-right: 10px;">
|
||||
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown" bs-tooltip="'Datasource'">
|
||||
<i class="fa fa-database"></i>
|
||||
{{datasource.name}} <span class="caret"></span>
|
||||
{{ctrl.datasource.name}} <span class="caret"></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="datasource in datasources" role="menuitem">
|
||||
<a ng-click="setDatasource(datasource);">{{datasource.name}}</a>
|
||||
<li ng-repeat="datasource in ctrl.datasources" role="menuitem">
|
||||
<a ng-click="ctrl.setDatasource(datasource);">{{datasource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -7,23 +7,23 @@
|
||||
Title
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-xlarge tight-form-input" ng-model='panel.title'></input>
|
||||
<input type="text" class="input-xlarge tight-form-input" ng-model='ctrl.panel.title'></input>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Span
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-mini tight-form-input" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
||||
<select class="input-mini tight-form-input" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Height
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input" ng-model='panel.height'></input>
|
||||
<input type="text" class="input-small tight-form-input" ng-model='ctrl.panel.height'></input>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<label class="checkbox-label" for="panel.transparent">Transparent</label>
|
||||
<input class="cr1" id="panel.transparent" type="checkbox" ng-model="panel.transparent" ng-checked="panel.transparent">
|
||||
<input class="cr1" id="panel.transparent" type="checkbox" ng-model="ctrl.panel.transparent" ng-checked="ctrl.panel.transparent">
|
||||
<label for="panel.transparent" class="cr1"></label>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -38,7 +38,7 @@
|
||||
Repeat Panel
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input last" ng-model="panel.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
|
||||
<select class="input-small tight-form-input last" ng-model="ctrl.panel.repeat" ng-options="f.name as f.name for f in ctrl.dashboard.templating.list">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</li>
|
||||
@@ -46,7 +46,7 @@
|
||||
Min span
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input last" ng-model="panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
|
||||
<select class="input-small tight-form-input last" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</li>
|
||||
@@ -56,6 +56,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<panel-links-editor panel="panel"></panel-links-editor>
|
||||
<panel-links-editor panel="ctrl.panel"></panel-links-editor>
|
||||
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="ctrl.duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="ctrl.moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="ctrl.moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -25,7 +25,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item"
|
||||
ng-click="target.hide = !target.hide; get_data();"
|
||||
ng-click="target.hide = !target.hide; ctrl.refresh();"
|
||||
role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
@@ -35,4 +35,4 @@
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<cloudwatch-query-parameter target="target" datasource="datasource" on-change="refreshMetricData()"></cloudwatch-query-parameter>
|
||||
<cloudwatch-query-parameter target="target" datasource="ctrl.datasource" on-change="refreshMetricData()"></cloudwatch-query-parameter>
|
||||
|
||||
@@ -16,7 +16,7 @@ function (angular, _) {
|
||||
$scope.refreshMetricData = function() {
|
||||
if (!_.isEqual($scope.oldTarget, $scope.target)) {
|
||||
$scope.oldTarget = angular.copy($scope.target);
|
||||
$scope.get_data();
|
||||
$scope.ctrl.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="panelCtrl.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -33,7 +33,7 @@
|
||||
{{target.refId}}
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; get_data();" role="menuitem">
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; panelCtrl.refresh();" role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -44,13 +44,13 @@
|
||||
Query
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 345px;" ng-model="target.query" spellcheck='false' placeholder="Lucene query" ng-blur="get_data()">
|
||||
<input type="text" class="tight-form-input" style="width: 345px;" ng-model="target.query" spellcheck='false' placeholder="Lucene query" ng-blur="panelCtrl.refresh()">
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Alias
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 200px;" ng-model="target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="get_data()">
|
||||
<input type="text" class="tight-form-input" style="width: 200px;" ng-model="target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="panelCtrl.refresh()">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Group by time interval
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input" ng-model="panel.interval" ng-blur="get_data();"
|
||||
<input type="text" class="input-medium tight-form-input" ng-model="ctrl.panel.interval" ng-blur="ctrl.refresh();"
|
||||
spellcheck='false' placeholder="example: >10s">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
@@ -23,7 +23,7 @@
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
alias patterns
|
||||
</a>
|
||||
</li>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="pull-left" style="margin-top: 30px;">
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 1">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 1">
|
||||
<h5>Alias patterns</h5>
|
||||
<ul ng-non-bindable>
|
||||
<li>{{term fieldname}} = replaced with value of term group by</li>
|
||||
|
||||
@@ -6,8 +6,9 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ElasticQueryCtrl', function($scope, $timeout, uiSegmentSrv) {
|
||||
module.controller('ElasticQueryCtrl', function($scope, $rootScope, $timeout, uiSegmentSrv) {
|
||||
$scope.esVersion = $scope.datasource.esVersion;
|
||||
$scope.panelCtrl = $scope.ctrl;
|
||||
|
||||
$scope.init = function() {
|
||||
var target = $scope.target;
|
||||
@@ -27,10 +28,10 @@ function (angular) {
|
||||
var newJson = angular.toJson($scope.datasource.queryBuilder.build($scope.target), true);
|
||||
if (newJson !== $scope.oldQueryRaw) {
|
||||
$scope.rawQueryOld = newJson;
|
||||
$scope.get_data();
|
||||
$scope.panelCtrl.refresh();
|
||||
}
|
||||
|
||||
$scope.appEvent('elastic-query-updated');
|
||||
$rootScope.appEvent('elastic-query-updated');
|
||||
};
|
||||
|
||||
$scope.handleQueryError = function(err) {
|
||||
|
||||
@@ -5,7 +5,10 @@ function (GraphiteDatasource) {
|
||||
'use strict';
|
||||
|
||||
function metricsQueryEditor() {
|
||||
return {controller: 'GraphiteQueryCtrl', templateUrl: 'app/plugins/datasource/graphite/partials/query.editor.html'};
|
||||
return {
|
||||
controller: 'GraphiteQueryCtrl',
|
||||
templateUrl: 'app/plugins/datasource/graphite/partials/query.editor.html'
|
||||
};
|
||||
}
|
||||
|
||||
function metricsQueryOptions() {
|
||||
|
||||
@@ -25,19 +25,19 @@
|
||||
</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a>
|
||||
<a tabindex="1" ng-click="ctrl.duplicateDataQuery(target)">Duplicate</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a>
|
||||
<a tabindex="1" ng-click="ctrl.moveDataQuery($index, $index-1)">Move up</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a>
|
||||
<a tabindex="1" ng-click="ctrl.moveDataQuery($index, $index+1)">Move down</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -48,14 +48,14 @@
|
||||
{{target.refId}}
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; get_data();" role="menuitem">
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; panelCtrl.refresh();" role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span style="display: block; overflow: hidden;">
|
||||
<input type="text" class="tight-form-clear-input" style="width: 100%;" ng-model="target.target" give-focus="target.textEditor" spellcheck='false' ng-model-onblur ng-change="get_data()" ng-show="target.textEditor"></input>
|
||||
<input type="text" class="tight-form-clear-input" style="width: 100%;" ng-model="target.target" give-focus="target.textEditor" spellcheck='false' ng-model-onblur ng-change="panelCtrl.getData()" ng-show="target.textEditor"></input>
|
||||
</span>
|
||||
|
||||
<ul class="tight-form-list" role="menu" ng-hide="target.textEditor">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li>
|
||||
<input type="text"
|
||||
class="input-mini tight-form-input"
|
||||
ng-model="panel.cacheTimeout"
|
||||
ng-model="ctrl.panel.cacheTimeout"
|
||||
bs-tooltip="'Graphite parameter to override memcache default timeout (unit is seconds)'"
|
||||
data-placement="right"
|
||||
spellcheck='false'
|
||||
@@ -23,10 +23,10 @@
|
||||
<li>
|
||||
<input type="text"
|
||||
class="input-mini tight-form-input"
|
||||
ng-model="panel.maxDataPoints"
|
||||
ng-model="ctrl.panel.maxDataPoints"
|
||||
bs-tooltip="'Override max data points, automatically set to graph width in pixels.'"
|
||||
data-placement="right"
|
||||
ng-model-onblur ng-change="get_data()"
|
||||
ng-model-onblur ng-change="ctrl.refresh()"
|
||||
spellcheck='false'
|
||||
placeholder="auto"></input>
|
||||
</li>
|
||||
@@ -39,27 +39,27 @@
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
shorter legend names
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
series as parameters
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
stacking
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(4)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(4)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
templating
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(5)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(5)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
max data points
|
||||
</a>
|
||||
</li>
|
||||
@@ -71,7 +71,7 @@
|
||||
<div class="editor-row">
|
||||
<div class="pull-left" style="margin-top: 30px;">
|
||||
|
||||
<div class="grafana-info-box span8" ng-if="editorHelpIndex === 1">
|
||||
<div class="grafana-info-box span8" ng-if="ctrl.editorHelpIndex === 1">
|
||||
<h5>Shorter legend names</h5>
|
||||
<ul>
|
||||
<li>alias() function to specify a custom series name</li>
|
||||
@@ -81,7 +81,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span8" ng-if="editorHelpIndex === 2">
|
||||
<div class="grafana-info-box span8" ng-if="ctrl.editorHelpIndex === 2">
|
||||
<h5>Series as parameter</h5>
|
||||
<ul>
|
||||
<li>Some graphite functions allow you to have many series arguments</li>
|
||||
@@ -99,7 +99,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 3">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 3">
|
||||
<h5>Stacking</h5>
|
||||
<ul>
|
||||
<li>You find the stacking option under Display Styles tab</li>
|
||||
@@ -107,7 +107,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 4">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 4">
|
||||
<h5>Templating</h5>
|
||||
<ul>
|
||||
<li>You can use a template variable in place of metric names</li>
|
||||
@@ -116,7 +116,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 5">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 5">
|
||||
<h5>Max data points</h5>
|
||||
<ul>
|
||||
<li>Every graphite request is issued with a maxDataPoints parameter</li>
|
||||
|
||||
@@ -11,6 +11,8 @@ function (angular, _, config, gfunc, Parser) {
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('GraphiteQueryCtrl', function($scope, uiSegmentSrv, templateSrv) {
|
||||
var panelCtrl = $scope.panelCtrl = $scope.ctrl;
|
||||
var datasource = $scope.datasource;
|
||||
|
||||
$scope.init = function() {
|
||||
if ($scope.target) {
|
||||
@@ -125,7 +127,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
}
|
||||
|
||||
var path = getSegmentPathUpTo(fromIndex + 1);
|
||||
return $scope.datasource.metricFindQuery(path)
|
||||
return datasource.metricFindQuery(path)
|
||||
.then(function(segments) {
|
||||
if (segments.length === 0) {
|
||||
if (path !== '') {
|
||||
@@ -159,7 +161,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
$scope.getAltSegments = function (index) {
|
||||
var query = index === 0 ? '*' : getSegmentPathUpTo(index) + '.*';
|
||||
|
||||
return $scope.datasource.metricFindQuery(query).then(function(segments) {
|
||||
return datasource.metricFindQuery(query).then(function(segments) {
|
||||
var altSegments = _.map(segments, function(segment) {
|
||||
return uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable });
|
||||
});
|
||||
@@ -208,7 +210,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
|
||||
$scope.targetTextChanged = function() {
|
||||
parseTarget();
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.targetChanged = function() {
|
||||
@@ -222,7 +224,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
|
||||
if ($scope.target.target !== oldTarget) {
|
||||
if ($scope.segments[$scope.segments.length - 1].value !== 'select metric') {
|
||||
$scope.$parent.get_data();
|
||||
panelCtrl.refresh();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,13 +14,18 @@ describe('GraphiteQueryCtrl', function() {
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
beforeEach(ctx.createControllerPhase('GraphiteQueryCtrl'));
|
||||
beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.scope.ctrl = {panel: ctx.panel};
|
||||
ctx.panelCtrl = ctx.scope.ctrl;
|
||||
ctx.scope.datasource = ctx.datasource;
|
||||
ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.controller = $controller('GraphiteQueryCtrl', {$scope: ctx.scope});
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
ctx.scope.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};
|
||||
|
||||
ctx.scope.datasource = ctx.datasource;
|
||||
ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
@@ -49,7 +54,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.scope.addFunction(gfunc.getFuncDef('aliasByNode'));
|
||||
});
|
||||
|
||||
@@ -61,8 +66,8 @@ describe('GraphiteQueryCtrl', function() {
|
||||
expect(ctx.scope.target.target).to.be('aliasByNode(test.prod.*.count, 2)');
|
||||
});
|
||||
|
||||
it('should call get_data', function() {
|
||||
expect(ctx.scope.$parent.get_data.called).to.be(true);
|
||||
it('should call refresh', function() {
|
||||
expect(ctx.panelCtrl.refresh.called).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,8 +77,6 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: true}]));
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
ctx.scope.addFunction(gfunc.getFuncDef('asPercent'));
|
||||
});
|
||||
|
||||
@@ -88,7 +91,6 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
});
|
||||
|
||||
it('should not add select metric segment', function() {
|
||||
@@ -107,7 +109,6 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
});
|
||||
|
||||
it('should add 2 segments', function() {
|
||||
@@ -147,7 +148,6 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.altSegments = results;
|
||||
});
|
||||
ctx.scope.$digest();
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
});
|
||||
|
||||
it('should have no segments', function() {
|
||||
@@ -162,7 +162,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.scope.target.target = '';
|
||||
ctx.scope.targetChanged();
|
||||
});
|
||||
@@ -171,8 +171,8 @@ describe('GraphiteQueryCtrl', function() {
|
||||
expect(ctx.scope.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)');
|
||||
});
|
||||
|
||||
it('should call get_data', function() {
|
||||
expect(ctx.scope.$parent.get_data.called).to.be(true);
|
||||
it('should call panelCtrl.refresh', function() {
|
||||
expect(ctx.panelCtrl.refresh.called).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="panelCtrl.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -35,7 +35,7 @@
|
||||
{{target.refId}}
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; get_data();" role="menuitem">
|
||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; panelCtrl.refresh();" role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -60,7 +60,7 @@
|
||||
</ul>
|
||||
|
||||
<div class="tight-form-flex-wrapper" ng-show="target.rawQuery">
|
||||
<input type="text" class="tight-form-clear-input" ng-model="target.query" spellcheck="false" style="width: 100%;" ng-blur="get_data()"></input>
|
||||
<input type="text" class="tight-form-clear-input" ng-model="target.query" spellcheck="false" style="width: 100%;" ng-blur="panelCtrl.refresh()"></input>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
@@ -88,7 +88,7 @@
|
||||
<span>GROUP BY</span>
|
||||
</li>
|
||||
<li ng-repeat="part in queryModel.groupByParts">
|
||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="removeGroupByPart(part, $index)" part-updated="get_data();" get-options="getPartOptions(part)"></influx-query-part-editor>
|
||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="removeGroupByPart(part, $index)" part-updated="panelCtrl.refresh();" get-options="getPartOptions(part)"></influx-query-part-editor>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="groupBySegment" get-options="getGroupByOptions()" on-change="groupByAction(part, $index)"></metric-segment>
|
||||
@@ -104,13 +104,13 @@
|
||||
ALIAS BY
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="get_data()">
|
||||
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="panelCtrl.refresh()">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Format as
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" style="width: 104px" ng-model="target.resultFormat" ng-options="f.value as f.text for f in resultFormats" ng-change="get_data()"></select>
|
||||
<select class="input-small tight-form-input" style="width: 104px" ng-model="target.resultFormat" ng-options="f.value as f.text for f in resultFormats" ng-change="panelCtrl.refresh()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Group by time interval
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input" ng-model="panel.interval" ng-blur="get_data();"
|
||||
<input type="text" class="input-medium tight-form-input" ng-model="ctrl.panel.interval" ng-blur="ctrl.refresh();"
|
||||
spellcheck='false' placeholder="example: >10s">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
@@ -24,17 +24,17 @@
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
alias patterns
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(2)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(2)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
stacking & and fill
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a ng-click="toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<a ng-click="ctrl.toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
group by time
|
||||
</a>
|
||||
</li>
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="editor-row">
|
||||
<div class="pull-left" style="margin-top: 30px;">
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 1">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 1">
|
||||
<h5>Alias patterns</h5>
|
||||
<ul>
|
||||
<li>$m = replaced with measurement name</li>
|
||||
@@ -58,7 +58,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 2">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 2">
|
||||
<h5>Stacking and fill</h5>
|
||||
<ul>
|
||||
<li>When stacking is enabled it important that points align</li>
|
||||
@@ -69,7 +69,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 3">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.editorHelpIndex === 3">
|
||||
<h5>Group by time</h5>
|
||||
<ul>
|
||||
<li>Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana</li>
|
||||
@@ -80,8 +80,6 @@
|
||||
<li>Example: >60s if you write metrics to InfluxDB every 60 seconds</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,13 +15,16 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
queryPart = queryPart.default;
|
||||
|
||||
module.controller('InfluxQueryCtrl', function($scope, templateSrv, $q, uiSegmentSrv) {
|
||||
var panelCtrl = $scope.ctrl;
|
||||
var datasource = $scope.datasource;
|
||||
$scope.panelCtrl = panelCtrl;
|
||||
|
||||
$scope.init = function() {
|
||||
if (!$scope.target) { return; }
|
||||
|
||||
$scope.target = $scope.target;
|
||||
$scope.queryModel = new InfluxQuery($scope.target);
|
||||
$scope.queryBuilder = new InfluxQueryBuilder($scope.target, $scope.datasource.database);
|
||||
$scope.queryBuilder = new InfluxQueryBuilder($scope.target, datasource.database);
|
||||
$scope.groupBySegment = uiSegmentSrv.newPlusButton();
|
||||
$scope.resultFormats = [
|
||||
{text: 'Time series', value: 'time_series'},
|
||||
@@ -75,7 +78,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
$scope.getGroupByOptions = function() {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return datasource.metricFindQuery(query)
|
||||
.then(function(tags) {
|
||||
var options = [];
|
||||
if (!$scope.queryModel.hasFill()) {
|
||||
@@ -97,26 +100,26 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
var plusButton = uiSegmentSrv.newPlusButton();
|
||||
$scope.groupBySegment.value = plusButton.value;
|
||||
$scope.groupBySegment.html = plusButton.html;
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.removeGroupByPart = function(part, index) {
|
||||
$scope.queryModel.removeGroupByPart(part, index);
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.addSelectPart = function(selectParts, cat, subitem) {
|
||||
$scope.queryModel.addSelectPart(selectParts, subitem.value);
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.removeSelectPart = function(selectParts, part) {
|
||||
$scope.queryModel.removeSelectPart(selectParts, part);
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.selectPartUpdated = function() {
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.fixTagSegments = function() {
|
||||
@@ -130,19 +133,19 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.measurementChanged = function() {
|
||||
$scope.target.measurement = $scope.measurementSegment.value;
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.getPolicySegments = function() {
|
||||
var policiesQuery = $scope.queryBuilder.buildExploreQuery('RETENTION POLICIES');
|
||||
return $scope.datasource.metricFindQuery(policiesQuery)
|
||||
return datasource.metricFindQuery(policiesQuery)
|
||||
.then($scope.transformToSegments(false))
|
||||
.then(null, $scope.handleQueryError);
|
||||
};
|
||||
|
||||
$scope.policyChanged = function() {
|
||||
$scope.target.policy = $scope.policySegment.value;
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.toggleQueryMode = function () {
|
||||
@@ -151,19 +154,19 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.getMeasurements = function () {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('MEASUREMENTS');
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments(true), $scope.handleQueryError);
|
||||
};
|
||||
|
||||
$scope.getPartOptions = function(part) {
|
||||
if (part.def.type === 'field') {
|
||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
|
||||
return $scope.datasource.metricFindQuery(fieldsQuery)
|
||||
return datasource.metricFindQuery(fieldsQuery)
|
||||
.then($scope.transformToSegments(true), $scope.handleQueryError);
|
||||
}
|
||||
if (part.def.type === 'tag') {
|
||||
var tagsQuery = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
return $scope.datasource.metricFindQuery(tagsQuery)
|
||||
return datasource.metricFindQuery(tagsQuery)
|
||||
.then($scope.transformToSegments(true), $scope.handleQueryError);
|
||||
}
|
||||
};
|
||||
@@ -211,7 +214,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
addTemplateVars = true;
|
||||
}
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments(addTemplateVars))
|
||||
.then(function(results) {
|
||||
if (segment.type === 'key') {
|
||||
@@ -224,7 +227,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.getFieldSegments = function() {
|
||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
|
||||
return $scope.datasource.metricFindQuery(fieldsQuery)
|
||||
return datasource.metricFindQuery(fieldsQuery)
|
||||
.then($scope.transformToSegments(false))
|
||||
.then(null, $scope.handleQueryError);
|
||||
};
|
||||
@@ -234,7 +237,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.setFill = function(fill) {
|
||||
$scope.target.fill = fill;
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.tagSegmentUpdated = function(segment, index) {
|
||||
@@ -300,7 +303,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
});
|
||||
|
||||
$scope.target.tags = tags;
|
||||
$scope.$parent.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.getTagValueOperator = function(tagValue, tagOperator) {
|
||||
|
||||
@@ -10,14 +10,20 @@ describe('InfluxDBQueryCtrl', function() {
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(ctx.providePhase());
|
||||
beforeEach(ctx.createControllerPhase('InfluxQueryCtrl'));
|
||||
|
||||
beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.scope.ctrl = {panel: ctx.panel};
|
||||
ctx.scope.datasource = ctx.datasource;
|
||||
ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl = ctx.scope.ctrl;
|
||||
ctx.controller = $controller('InfluxQueryCtrl', {$scope: ctx.scope});
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
ctx.scope.target = {};
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
|
||||
ctx.scope.datasource = ctx.datasource;
|
||||
ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="panelCtrl.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -29,7 +28,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item"
|
||||
ng-click="target.hide = !target.hide; get_data();"
|
||||
ng-click="target.hide = !target.hide; panelCtrl.refresh();"
|
||||
role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
|
||||
@@ -9,6 +9,7 @@ function (angular, _, kbn) {
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('OpenTSDBQueryCtrl', function($scope) {
|
||||
$scope.panelCtrl = $scope.ctrl;
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.target.errors = validateTarget($scope.target);
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.duplicateDataQuery(target)">Duplicate</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index-1)">Move up</a></li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index+1)">Move down</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
|
||||
<a class="pointer" tabindex="1" ng-click="panelCtr.removeDataQuery(target)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -29,7 +28,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a class="tight-form-item"
|
||||
ng-click="target.hide = !target.hide; get_data();"
|
||||
ng-click="target.hide = !target.hide; panelCtrl.refresh();"
|
||||
role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
|
||||
@@ -8,6 +8,8 @@ function (angular, _) {
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('PrometheusQueryCtrl', function($scope, templateSrv) {
|
||||
$scope.panelCtrl = $scope.ctrl;
|
||||
$scope.panel = $scope.panelCtrl.panel;
|
||||
|
||||
$scope.init = function() {
|
||||
var target = $scope.target;
|
||||
@@ -29,7 +31,7 @@ function (angular, _) {
|
||||
$scope.refreshMetricData = function() {
|
||||
if (!_.isEqual($scope.oldTarget, $scope.target)) {
|
||||
$scope.oldTarget = angular.copy($scope.target);
|
||||
$scope.get_data();
|
||||
$scope.paneCtrl.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<strong>Mode</strong>
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input last" ng-model="panel.mode" ng-options="f for f in modes" ng-change="get_data()"></select>
|
||||
<select class="input-small tight-form-input last" ng-model="ctrl.panel.mode" ng-options="f for f in ctrl.modes" ng-change="ctrl.refresh()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" style="margin-bottom: 20px" ng-if="panel.mode === 'search'">
|
||||
<div class="section" style="margin-bottom: 20px" ng-if="ctrl.panel.mode === 'search'">
|
||||
<div class="tight-form last">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 110px">
|
||||
@@ -24,13 +24,13 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input" placeholder="title query"
|
||||
ng-model="panel.query" ng-change="get_data()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Tags
|
||||
</li>
|
||||
<li>
|
||||
<bootstrap-tagsinput ng-model="panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="get_data()">
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
|
||||
</bootstrap-tagsinput>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -47,7 +47,7 @@
|
||||
<strong>Limit number to</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input class="input-small tight-form-input last" type="number" ng-model="panel.limit" ng-model-onblur ng-change="get_data()">
|
||||
<input class="input-small tight-form-input last" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<grafana-panel>
|
||||
<div class="dashlist">
|
||||
<div class="dashlist-item" ng-repeat="dash in dashList">
|
||||
<div class="dashlist">
|
||||
<div class="dashlist-item" ng-repeat="dash in ctrl.dashList">
|
||||
<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}">
|
||||
<span class="dashlist-title">
|
||||
{{dash.title}}
|
||||
@@ -10,4 +9,4 @@
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</grafana-panel>
|
||||
</div>
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'app/app',
|
||||
'lodash',
|
||||
'app/core/config',
|
||||
'app/features/panel/panel_meta',
|
||||
],
|
||||
function (angular, app, _, config, PanelMeta) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.dashlist', []);
|
||||
app.useModule(module);
|
||||
|
||||
/** @ngInject */
|
||||
function DashListPanelCtrl($scope, panelSrv, backendSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Dashboard list',
|
||||
editIcon: "fa fa-star",
|
||||
fullscreen: true,
|
||||
});
|
||||
|
||||
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/dashlist/editor.html');
|
||||
|
||||
var defaults = {
|
||||
mode: 'starred',
|
||||
query: '',
|
||||
limit: 10,
|
||||
tags: []
|
||||
};
|
||||
|
||||
$scope.modes = ['starred', 'search'];
|
||||
|
||||
_.defaults($scope.panel, defaults);
|
||||
|
||||
$scope.dashList = [];
|
||||
|
||||
$scope.init = function() {
|
||||
panelSrv.init($scope);
|
||||
|
||||
if ($scope.panel.tag) {
|
||||
$scope.panel.tags = [$scope.panel.tag];
|
||||
delete $scope.panel.tag;
|
||||
}
|
||||
|
||||
if ($scope.isNewPanel()) {
|
||||
$scope.panel.title = "Starred Dashboards";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.refreshData = function() {
|
||||
var params = {
|
||||
limit: $scope.panel.limit
|
||||
};
|
||||
|
||||
if ($scope.panel.mode === 'starred') {
|
||||
params.starred = "true";
|
||||
} else {
|
||||
params.query = $scope.panel.query;
|
||||
params.tag = $scope.panel.tags;
|
||||
}
|
||||
|
||||
return backendSrv.search(params).then(function(result) {
|
||||
$scope.dashList = result;
|
||||
$scope.panelRenderingComplete();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
}
|
||||
|
||||
function dashListPanelDirective() {
|
||||
return {
|
||||
controller: DashListPanelCtrl,
|
||||
templateUrl: 'app/plugins/panel/dashlist/module.html',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
panel: dashListPanelDirective
|
||||
};
|
||||
});
|
||||
63
public/app/plugins/panel/dashlist/module.ts
Normal file
63
public/app/plugins/panel/dashlist/module.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
mode: 'starred',
|
||||
query: '',
|
||||
limit: 10,
|
||||
tags: []
|
||||
};
|
||||
|
||||
class DashListCtrl extends PanelCtrl {
|
||||
dashList: any[];
|
||||
modes: any[];
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private backendSrv) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
|
||||
if (this.panel.tag) {
|
||||
this.panel.tags = [$scope.panel.tag];
|
||||
delete this.panel.tag;
|
||||
}
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
this.modes = ['starred', 'search'];
|
||||
this.icon = "fa fa-star";
|
||||
this.addEditorTab('Options', () => {
|
||||
return {templateUrl: 'app/plugins/panel/dashlist/editor.html'};
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
var params: any = {limit: this.panel.limit};
|
||||
|
||||
if (this.panel.mode === 'starred') {
|
||||
params.starred = "true";
|
||||
} else {
|
||||
params.query = this.panel.query;
|
||||
params.tag = this.panel.tags;
|
||||
}
|
||||
|
||||
return this.backendSrv.search(params).then(result => {
|
||||
this.dashList = result;
|
||||
this.renderingCompleted();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DashListPanel extends PanelDirective {
|
||||
controller = DashListCtrl;
|
||||
templateUrl = 'public/app/plugins/panel/dashlist/module.html';
|
||||
}
|
||||
|
||||
export {
|
||||
DashListCtrl,
|
||||
DashListPanel as Panel
|
||||
}
|
||||
@@ -10,22 +10,22 @@
|
||||
Unit
|
||||
</li>
|
||||
<li class="dropdown" style="width: 140px;"
|
||||
ng-model="panel.y_formats[0]"
|
||||
dropdown-typeahead="unitFormats"
|
||||
dropdown-typeahead-on-select="setUnitFormat(0, $subItem)">
|
||||
ng-model="ctrl.panel.y_formats[0]"
|
||||
dropdown-typeahead="ctrl.unitFormats"
|
||||
dropdown-typeahead-on-select="ctrl.setUnitFormat(0, $subItem)">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Scale type
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" style="width: 113px" ng-model="panel.grid.leftLogBase" ng-options="v as k for (k, v) in logScales" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" style="width: 113px" ng-model="ctrl.panel.grid.leftLogBase" ng-options="v as k for (k, v) in ctrl.logScales" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Label
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input last"
|
||||
ng-model="panel.leftYAxisLabel" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.leftYAxisLabel" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -40,16 +40,16 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" placeholder="auto"
|
||||
empty-to-null ng-model="panel.grid.leftMax"
|
||||
ng-change="render()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.grid.leftMax"
|
||||
ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 115px; text-align: right;">
|
||||
Y-Min
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" placeholder="auto" style="width: 113px;"
|
||||
empty-to-null ng-model="panel.grid.leftMin"
|
||||
ng-change="render()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.grid.leftMin"
|
||||
ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -63,22 +63,22 @@
|
||||
Unit
|
||||
</li>
|
||||
<li class="dropdown" style="width: 140px"
|
||||
ng-model="panel.y_formats[1]"
|
||||
dropdown-typeahead="unitFormats"
|
||||
dropdown-typeahead-on-select="setUnitFormat(1, $subItem)">
|
||||
ng-model="ctrl.panel.y_formats[1]"
|
||||
dropdown-typeahead="ctrl.unitFormats"
|
||||
dropdown-typeahead-on-select="ctrl.setUnitFormat(1, $subItem)">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Scale type
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" style="width: 113px" ng-model="panel.grid.rightLogBase" ng-options="v as k for (k, v) in logScales" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" style="width: 113px" ng-model="ctrl.panel.grid.rightLogBase" ng-options="v as k for (k, v) in ctrl.logScales" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Label
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input last"
|
||||
ng-model="panel.rightYAxisLabel" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.rightYAxisLabel" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -93,16 +93,16 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" placeholder="auto"
|
||||
empty-to-null ng-model="panel.grid.rightMax"
|
||||
ng-change="render()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.grid.rightMax"
|
||||
ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 115px; text-align: right;">
|
||||
Y-Min
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" placeholder="auto" style="width: 113px;"
|
||||
empty-to-null ng-model="panel.grid.rightMin"
|
||||
ng-change="render()" ng-model-onblur>
|
||||
empty-to-null ng-model="ctrl.panel.grid.rightMin"
|
||||
ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -119,13 +119,13 @@
|
||||
<li class="tight-form-item">
|
||||
X-Axis
|
||||
<input class="cr1" id="hideXAxis" type="checkbox"
|
||||
ng-model="panel['x-axis']" ng-checked="panel['x-axis']" ng-change="render()">
|
||||
ng-model="ctrl.panel['x-axis']" ng-checked="ctrl.panel['x-axis']" ng-change="ctrl.render()">
|
||||
<label for="hideXAxis" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
Y-Axis
|
||||
<input class="cr1" id="hideYAxis" type="checkbox"
|
||||
ng-model="panel['y-axis']" ng-checked="panel['y-axis']" ng-change="render()">
|
||||
ng-model="ctrl.panel['y-axis']" ng-checked="ctrl.panel['y-axis']" ng-change="ctrl.render()">
|
||||
<label for="hideYAxis" class="cr1"></label>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -141,23 +141,23 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input"
|
||||
ng-model="panel.grid.threshold1" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.grid.threshold1" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<spectrum-picker ng-model="panel.grid.threshold1Color" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.grid.threshold1Color" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Level 2
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input"
|
||||
ng-model="panel.grid.threshold2" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.grid.threshold2" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<spectrum-picker ng-model="panel.grid.threshold2Color" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.grid.threshold2Color" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<editor-checkbox text="Line mode" model="panel.grid.thresholdLine" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="Line mode" model="ctrl.panel.grid.thresholdLine" change="ctrl.render()"></editor-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -173,20 +173,20 @@
|
||||
Legend
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Show" model="panel.legend.show" change="get_data()"></editor-checkbox>
|
||||
<editor-checkbox text="Show" model="ctrl.panel.legend.show" change="ctrl.refresh()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Table" model="panel.legend.alignAsTable" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="Table" model="ctrl.panel.legend.alignAsTable" change="ctrl.render()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Right side" model="panel.legend.rightSide" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="Right side" model="ctrl.panel.legend.rightSide" change="ctrl.render()"></editor-checkbox>
|
||||
</li>
|
||||
<li ng-if="panel.legend.rightSide" class="tight-form-item">
|
||||
Side width
|
||||
</li>
|
||||
<li ng-if="panel.legend.rightSide" style="width: 105px">
|
||||
<input type="number" class="input-small tight-form-input" placeholder="250" bs-tooltip="'Set a min-width for the legend side table/block'" data-placement="right"
|
||||
ng-model="panel.legend.sideWidth" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.legend.sideWidth" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -197,10 +197,10 @@
|
||||
Hide series
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="With only nulls" model="panel.legend.hideEmpty" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="With only nulls" model="ctrl.panel.legend.hideEmpty" change="ctrl.render()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<editor-checkbox text="With only zeroes" model="panel.legend.hideZero" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="With only zeroes" model="ctrl.panel.legend.hideZero" change="ctrl.render()"></editor-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -212,26 +212,26 @@
|
||||
Values
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Min" model="panel.legend.min" change="legendValuesOptionChanged()"></editor-checkbox>
|
||||
<editor-checkbox text="Min" model="ctrl.panel.legend.min" change="ctrl.legendValuesOptionChanged()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Max" model="panel.legend.max" change="legendValuesOptionChanged()"></editor-checkbox>
|
||||
<editor-checkbox text="Max" model="ctrl.panel.legend.max" change="ctrl.legendValuesOptionChanged()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Avg" model="panel.legend.avg" change="legendValuesOptionChanged()"></editor-checkbox>
|
||||
<editor-checkbox text="Avg" model="ctrl.panel.legend.avg" change="ctrl.legendValuesOptionChanged()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Current" model="panel.legend.current" change="legendValuesOptionChanged()"></editor-checkbox>
|
||||
<editor-checkbox text="Current" model="ctrl.panel.legend.current" change="ctrl.legendValuesOptionChanged()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Total" model="panel.legend.total" change="legendValuesOptionChanged()"></editor-checkbox>
|
||||
<editor-checkbox text="Total" model="ctrl.panel.legend.total" change="ctrl.legendValuesOptionChanged()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Decimals
|
||||
</li>
|
||||
<li style="width: 105px">
|
||||
<input type="number" class="input-small tight-form-input" placeholder="auto" bs-tooltip="'Override automatic decimal precision for legend and tooltips'" data-placement="right"
|
||||
ng-model="panel.decimals" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.decimals" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -24,14 +24,16 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
restrict: 'A',
|
||||
template: '<div> </div>',
|
||||
link: function(scope, elem) {
|
||||
var dashboard = scope.dashboard;
|
||||
var ctrl = scope.ctrl;
|
||||
var dashboard = ctrl.dashboard;
|
||||
var panel = ctrl.panel;
|
||||
var data, annotations;
|
||||
var sortedSeries;
|
||||
var graphHeight;
|
||||
var legendSideLastValue = null;
|
||||
scope.crosshairEmiter = false;
|
||||
var rootScope = scope.$root;
|
||||
|
||||
scope.onAppEvent('setCrosshair', function(event, info) {
|
||||
rootScope.onAppEvent('setCrosshair', function(event, info) {
|
||||
// do not need to to this if event is from this panel
|
||||
if (info.scope === scope) {
|
||||
return;
|
||||
@@ -43,20 +45,20 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
plot.setCrosshair({ x: info.pos.x, y: info.pos.y });
|
||||
}
|
||||
}
|
||||
});
|
||||
}, scope);
|
||||
|
||||
scope.onAppEvent('clearCrosshair', function() {
|
||||
rootScope.onAppEvent('clearCrosshair', function() {
|
||||
var plot = elem.data().plot;
|
||||
if (plot) {
|
||||
plot.clearCrosshair();
|
||||
}
|
||||
});
|
||||
}, scope);
|
||||
|
||||
// Receive render events
|
||||
scope.$on('render',function(event, renderData) {
|
||||
data = renderData || data;
|
||||
if (!data) {
|
||||
scope.get_data();
|
||||
ctrl.refresh();
|
||||
return;
|
||||
}
|
||||
annotations = data.annotations || annotations;
|
||||
@@ -64,10 +66,10 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
});
|
||||
|
||||
function getLegendHeight(panelHeight) {
|
||||
if (!scope.panel.legend.show || scope.panel.legend.rightSide) {
|
||||
if (!panel.legend.show || panel.legend.rightSide) {
|
||||
return 0;
|
||||
}
|
||||
if (scope.panel.legend.alignAsTable) {
|
||||
if (panel.legend.alignAsTable) {
|
||||
var total = 30 + (25 * data.length);
|
||||
return Math.min(total, Math.floor(panelHeight/2));
|
||||
} else {
|
||||
@@ -77,14 +79,13 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
|
||||
function setElementHeight() {
|
||||
try {
|
||||
graphHeight = scope.height || scope.panel.height || scope.row.height;
|
||||
graphHeight = ctrl.height || panel.height || ctrl.row.height;
|
||||
if (_.isString(graphHeight)) {
|
||||
graphHeight = parseInt(graphHeight.replace('px', ''), 10);
|
||||
}
|
||||
|
||||
graphHeight -= 5; // padding
|
||||
graphHeight -= scope.panel.title ? 24 : 9; // subtract panel title bar
|
||||
|
||||
graphHeight -= panel.title ? 24 : 9; // subtract panel title bar
|
||||
graphHeight = graphHeight - getLegendHeight(graphHeight); // subtract one line legend
|
||||
|
||||
elem.css('height', graphHeight + 'px');
|
||||
@@ -100,7 +101,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($rootScope.fullscreen && !scope.fullscreen) {
|
||||
if (ctrl.otherPanelInFullscreenMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -122,11 +123,11 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
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]];
|
||||
var formater = kbn.valueFormats[panel.y_formats[series.yaxis - 1]];
|
||||
|
||||
// decimal override
|
||||
if (_.isNumber(scope.panel.decimals)) {
|
||||
series.updateLegendValues(formater, scope.panel.decimals, null);
|
||||
if (_.isNumber(panel.decimals)) {
|
||||
series.updateLegendValues(formater, panel.decimals, null);
|
||||
} else {
|
||||
// auto decimals
|
||||
// legend and tooltip gets one more decimal precision
|
||||
@@ -135,22 +136,22 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
series.updateLegendValues(formater, tickDecimals, axis.scaledDecimals + 2);
|
||||
}
|
||||
|
||||
if(!scope.$$phase) { scope.$digest(); }
|
||||
if(!rootScope.$$phase) { scope.$digest(); }
|
||||
}
|
||||
|
||||
// add left axis labels
|
||||
if (scope.panel.leftYAxisLabel) {
|
||||
if (panel.leftYAxisLabel) {
|
||||
var yaxisLabel = $("<div class='axisLabel left-yaxis-label'></div>")
|
||||
.text(scope.panel.leftYAxisLabel)
|
||||
.text(panel.leftYAxisLabel)
|
||||
.appendTo(elem);
|
||||
|
||||
yaxisLabel.css("margin-top", yaxisLabel.width() / 2);
|
||||
}
|
||||
|
||||
// add right axis labels
|
||||
if (scope.panel.rightYAxisLabel) {
|
||||
if (panel.rightYAxisLabel) {
|
||||
var rightLabel = $("<div class='axisLabel right-yaxis-label'></div>")
|
||||
.text(scope.panel.rightYAxisLabel)
|
||||
.text(panel.rightYAxisLabel)
|
||||
.appendTo(elem);
|
||||
|
||||
rightLabel.css("margin-top", rightLabel.width() / 2);
|
||||
@@ -158,8 +159,8 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
}
|
||||
|
||||
function processOffsetHook(plot, gridMargin) {
|
||||
if (scope.panel.leftYAxisLabel) { gridMargin.left = 20; }
|
||||
if (scope.panel.rightYAxisLabel) { gridMargin.right = 20; }
|
||||
if (panel.leftYAxisLabel) { gridMargin.left = 20; }
|
||||
if (panel.rightYAxisLabel) { gridMargin.right = 20; }
|
||||
}
|
||||
|
||||
// Function for rendering panel
|
||||
@@ -168,7 +169,6 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
var panel = scope.panel;
|
||||
var stack = panel.stack ? true : null;
|
||||
|
||||
// Populate element
|
||||
@@ -230,7 +230,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode, panel.y_formats);
|
||||
|
||||
// if hidden remove points and disable stack
|
||||
if (scope.hiddenSeries[series.alias]) {
|
||||
if (ctrl.hiddenSeries[series.alias]) {
|
||||
series.data = [];
|
||||
series.stack = false;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
}
|
||||
|
||||
if (incrementRenderCounter) {
|
||||
scope.panelRenderingComplete();
|
||||
ctrl.renderingCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,12 +285,12 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
|
||||
function addTimeAxis(options) {
|
||||
var ticks = elem.width() / 100;
|
||||
var min = _.isUndefined(scope.range.from) ? null : scope.range.from.valueOf();
|
||||
var max = _.isUndefined(scope.range.to) ? null : scope.range.to.valueOf();
|
||||
var min = _.isUndefined(ctrl.range.from) ? null : ctrl.range.from.valueOf();
|
||||
var max = _.isUndefined(ctrl.range.to) ? null : ctrl.range.to.valueOf();
|
||||
|
||||
options.xaxis = {
|
||||
timezone: dashboard.timezone,
|
||||
show: scope.panel['x-axis'],
|
||||
show: panel['x-axis'],
|
||||
mode: "time",
|
||||
min: min,
|
||||
max: max,
|
||||
@@ -361,11 +361,11 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
function configureAxisOptions(data, options) {
|
||||
var defaults = {
|
||||
position: 'left',
|
||||
show: scope.panel['y-axis'],
|
||||
min: scope.panel.grid.leftMin,
|
||||
show: panel['y-axis'],
|
||||
min: panel.grid.leftMin,
|
||||
index: 1,
|
||||
logBase: scope.panel.grid.leftLogBase || 1,
|
||||
max: scope.panel.percentage && scope.panel.stack ? 100 : scope.panel.grid.leftMax,
|
||||
logBase: panel.grid.leftLogBase || 1,
|
||||
max: panel.percentage && panel.stack ? 100 : panel.grid.leftMax,
|
||||
};
|
||||
|
||||
options.yaxes.push(defaults);
|
||||
@@ -373,18 +373,18 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
if (_.findWhere(data, {yaxis: 2})) {
|
||||
var secondY = _.clone(defaults);
|
||||
secondY.index = 2,
|
||||
secondY.logBase = scope.panel.grid.rightLogBase || 1,
|
||||
secondY.logBase = panel.grid.rightLogBase || 1,
|
||||
secondY.position = 'right';
|
||||
secondY.min = scope.panel.grid.rightMin;
|
||||
secondY.max = scope.panel.percentage && scope.panel.stack ? 100 : scope.panel.grid.rightMax;
|
||||
secondY.min = panel.grid.rightMin;
|
||||
secondY.max = panel.percentage && panel.stack ? 100 : panel.grid.rightMax;
|
||||
options.yaxes.push(secondY);
|
||||
|
||||
applyLogScale(options.yaxes[1], data);
|
||||
configureAxisMode(options.yaxes[1], scope.panel.percentage && scope.panel.stack ? "percent" : scope.panel.y_formats[1]);
|
||||
configureAxisMode(options.yaxes[1], panel.percentage && panel.stack ? "percent" : panel.y_formats[1]);
|
||||
}
|
||||
|
||||
applyLogScale(options.yaxes[0], data);
|
||||
configureAxisMode(options.yaxes[0], scope.panel.percentage && scope.panel.stack ? "percent" : scope.panel.y_formats[0]);
|
||||
configureAxisMode(options.yaxes[0], panel.percentage && panel.stack ? "percent" : panel.y_formats[0]);
|
||||
}
|
||||
|
||||
function applyLogScale(axis, data) {
|
||||
@@ -466,18 +466,18 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
url += '&height=' + elem.css('height').replace('px', '');
|
||||
url += '&bgcolor=1f1f1f'; // @grayDarker & @grafanaPanelBackground
|
||||
url += '&fgcolor=BBBFC2'; // @textColor & @grayLighter
|
||||
url += scope.panel.stack ? '&areaMode=stacked' : '';
|
||||
url += scope.panel.fill !== 0 ? ('&areaAlpha=' + (scope.panel.fill/10).toFixed(1)) : '';
|
||||
url += scope.panel.linewidth !== 0 ? '&lineWidth=' + scope.panel.linewidth : '';
|
||||
url += scope.panel.legend.show ? '&hideLegend=false' : '&hideLegend=true';
|
||||
url += scope.panel.grid.leftMin !== null ? '&yMin=' + scope.panel.grid.leftMin : '';
|
||||
url += scope.panel.grid.leftMax !== null ? '&yMax=' + scope.panel.grid.leftMax : '';
|
||||
url += scope.panel.grid.rightMin !== null ? '&yMin=' + scope.panel.grid.rightMin : '';
|
||||
url += scope.panel.grid.rightMax !== null ? '&yMax=' + scope.panel.grid.rightMax : '';
|
||||
url += scope.panel['x-axis'] ? '' : '&hideAxes=true';
|
||||
url += scope.panel['y-axis'] ? '' : '&hideYAxis=true';
|
||||
url += panel.stack ? '&areaMode=stacked' : '';
|
||||
url += panel.fill !== 0 ? ('&areaAlpha=' + (panel.fill/10).toFixed(1)) : '';
|
||||
url += panel.linewidth !== 0 ? '&lineWidth=' + panel.linewidth : '';
|
||||
url += panel.legend.show ? '&hideLegend=false' : '&hideLegend=true';
|
||||
url += panel.grid.leftMin !== null ? '&yMin=' + panel.grid.leftMin : '';
|
||||
url += panel.grid.leftMax !== null ? '&yMax=' + panel.grid.leftMax : '';
|
||||
url += panel.grid.rightMin !== null ? '&yMin=' + panel.grid.rightMin : '';
|
||||
url += panel.grid.rightMax !== null ? '&yMax=' + panel.grid.rightMax : '';
|
||||
url += panel['x-axis'] ? '' : '&hideAxes=true';
|
||||
url += panel['y-axis'] ? '' : '&hideYAxis=true';
|
||||
|
||||
switch(scope.panel.y_formats[0]) {
|
||||
switch(panel.y_formats[0]) {
|
||||
case 'bytes':
|
||||
url += '&yUnitSystem=binary';
|
||||
break;
|
||||
@@ -510,7 +510,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch(scope.panel.nullPointMode) {
|
||||
switch(panel.nullPointMode) {
|
||||
case 'connected':
|
||||
url += '&lineMode=connected';
|
||||
break;
|
||||
@@ -521,7 +521,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
break;
|
||||
}
|
||||
|
||||
url += scope.panel.steppedLine ? '&lineMode=staircase' : '';
|
||||
url += panel.steppedLine ? '&lineMode=staircase' : '';
|
||||
|
||||
elem.html('<img src="' + url + '"></img>');
|
||||
}
|
||||
|
||||
295
public/app/plugins/panel/graph/graph_ctrl.ts
Normal file
295
public/app/plugins/panel/graph/graph_ctrl.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import moment from 'moment';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import _ from 'lodash';
|
||||
import TimeSeries from '../../../core/time_series2';
|
||||
import * as fileExport from '../../../core/utils/file_export';
|
||||
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
||||
|
||||
var panelDefaults = {
|
||||
// datasource name, null = default datasource
|
||||
datasource: null,
|
||||
// sets client side (flot) or native graphite png renderer (png)
|
||||
renderer: 'flot',
|
||||
// Show/hide the x-axis
|
||||
'x-axis' : true,
|
||||
// Show/hide y-axis
|
||||
'y-axis' : true,
|
||||
// y axis formats, [left axis,right axis]
|
||||
y_formats : ['short', 'short'],
|
||||
// grid options
|
||||
grid : {
|
||||
leftLogBase: 1,
|
||||
leftMax: null,
|
||||
rightMax: null,
|
||||
leftMin: null,
|
||||
rightMin: null,
|
||||
rightLogBase: 1,
|
||||
threshold1: null,
|
||||
threshold2: null,
|
||||
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
||||
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
||||
},
|
||||
// show/hide lines
|
||||
lines : true,
|
||||
// fill factor
|
||||
fill : 1,
|
||||
// line width in pixels
|
||||
linewidth : 2,
|
||||
// show hide points
|
||||
points : false,
|
||||
// point radius in pixels
|
||||
pointradius : 5,
|
||||
// show hide bars
|
||||
bars : false,
|
||||
// enable/disable stacking
|
||||
stack : false,
|
||||
// stack percentage mode
|
||||
percentage : false,
|
||||
// legend options
|
||||
legend: {
|
||||
show: true, // disable/enable legend
|
||||
values: false, // disable/enable legend values
|
||||
min: false,
|
||||
max: false,
|
||||
current: false,
|
||||
total: false,
|
||||
avg: false
|
||||
},
|
||||
// how null points should be handled
|
||||
nullPointMode : 'connected',
|
||||
// staircase line mode
|
||||
steppedLine: false,
|
||||
// tooltip options
|
||||
tooltip : {
|
||||
value_type: 'cumulative',
|
||||
shared: true,
|
||||
},
|
||||
// time overrides
|
||||
timeFrom: null,
|
||||
timeShift: null,
|
||||
// metric queries
|
||||
targets: [{}],
|
||||
// series color overrides
|
||||
aliasColors: {},
|
||||
// other style overrides
|
||||
seriesOverrides: [],
|
||||
};
|
||||
|
||||
class GraphCtrl extends MetricsPanelCtrl {
|
||||
hiddenSeries: any = {};
|
||||
seriesList: any = [];
|
||||
logScales: any;
|
||||
unitFormats: any;
|
||||
annotationsPromise: any;
|
||||
datapointsCount: number;
|
||||
datapointsOutside: boolean;
|
||||
datapointsWarning: boolean;
|
||||
colors: any = [];
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private annotationsSrv) {
|
||||
super($scope, $injector);
|
||||
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
|
||||
_.defaults(this.panel.grid, panelDefaults.grid);
|
||||
_.defaults(this.panel.legend, panelDefaults.legend);
|
||||
|
||||
this.colors = $scope.$root.colors;
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
super.initEditMode();
|
||||
|
||||
this.icon = "fa fa-bar-chart";
|
||||
this.addEditorTab('Axes & Grid', 'public/app/plugins/panel/graph/axisEditor.html', 2);
|
||||
this.addEditorTab('Display Styles', 'public/app/plugins/panel/graph/styleEditor.html', 3);
|
||||
|
||||
this.logScales = {
|
||||
'linear': 1,
|
||||
'log (base 2)': 2,
|
||||
'log (base 10)': 10,
|
||||
'log (base 32)': 32,
|
||||
'log (base 1024)': 1024
|
||||
};
|
||||
this.unitFormats = kbn.getUnitFormats();
|
||||
}
|
||||
|
||||
getExtendedMenu() {
|
||||
var menu = super.getExtendedMenu();
|
||||
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
|
||||
menu.push({text: 'Toggle legend', click: 'ctrl.toggleLegend()'});
|
||||
return menu;
|
||||
}
|
||||
|
||||
setUnitFormat(axis, subItem) {
|
||||
this.panel.y_formats[axis] = subItem.value;
|
||||
this.render();
|
||||
}
|
||||
|
||||
refreshData(datasource) {
|
||||
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
|
||||
|
||||
return this.issueQueries(datasource)
|
||||
.then(res => this.dataHandler(res))
|
||||
.catch(err => {
|
||||
this.seriesList = [];
|
||||
this.render([]);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
zoomOut(evt) {
|
||||
this.publishAppEvent('zoom-out', evt);
|
||||
}
|
||||
|
||||
loadSnapshot(snapshotData) {
|
||||
this.updateTimeRange();
|
||||
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
|
||||
this.dataHandler(snapshotData);
|
||||
}
|
||||
|
||||
dataHandler(results) {
|
||||
// png renderer returns just a url
|
||||
if (_.isString(results)) {
|
||||
this.render(results);
|
||||
return;
|
||||
}
|
||||
|
||||
this.datapointsWarning = false;
|
||||
this.datapointsCount = 0;
|
||||
this.datapointsOutside = false;
|
||||
this.seriesList = _.map(results.data, (series, i) => this.seriesHandler(series, i));
|
||||
this.datapointsWarning = this.datapointsCount === 0 || this.datapointsOutside;
|
||||
|
||||
this.annotationsPromise.then(annotations => {
|
||||
this.loading = false;
|
||||
this.seriesList.annotations = annotations;
|
||||
this.render(this.seriesList);
|
||||
}, () => {
|
||||
this.loading = false;
|
||||
this.render(this.seriesList);
|
||||
});
|
||||
};
|
||||
|
||||
seriesHandler(seriesData, index) {
|
||||
var datapoints = seriesData.datapoints;
|
||||
var alias = seriesData.target;
|
||||
var colorIndex = index % this.colors.length;
|
||||
var color = this.panel.aliasColors[alias] || this.colors[colorIndex];
|
||||
|
||||
var series = new TimeSeries({
|
||||
datapoints: datapoints,
|
||||
alias: alias,
|
||||
color: color,
|
||||
});
|
||||
|
||||
if (datapoints && datapoints.length > 0) {
|
||||
var last = moment.utc(datapoints[datapoints.length - 1][1]);
|
||||
var from = moment.utc(this.range.from);
|
||||
if (last - from < -10000) {
|
||||
this.datapointsOutside = true;
|
||||
}
|
||||
|
||||
this.datapointsCount += datapoints.length;
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
render(data?: any) {
|
||||
this.broadcastRender(data);
|
||||
}
|
||||
|
||||
changeSeriesColor(series, color) {
|
||||
series.color = color;
|
||||
this.panel.aliasColors[series.alias] = series.color;
|
||||
this.render();
|
||||
}
|
||||
|
||||
toggleSeries(serie, event) {
|
||||
if (event.ctrlKey || event.metaKey || event.shiftKey) {
|
||||
if (this.hiddenSeries[serie.alias]) {
|
||||
delete this.hiddenSeries[serie.alias];
|
||||
} else {
|
||||
this.hiddenSeries[serie.alias] = true;
|
||||
}
|
||||
} else {
|
||||
this.toggleSeriesExclusiveMode(serie);
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
toggleSeriesExclusiveMode (serie) {
|
||||
var hidden = this.hiddenSeries;
|
||||
|
||||
if (hidden[serie.alias]) {
|
||||
delete hidden[serie.alias];
|
||||
}
|
||||
|
||||
// check if every other series is hidden
|
||||
var alreadyExclusive = _.every(this.seriesList, value => {
|
||||
if (value.alias === serie.alias) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hidden[value.alias];
|
||||
});
|
||||
|
||||
if (alreadyExclusive) {
|
||||
// remove all hidden series
|
||||
_.each(this.seriesList, value => {
|
||||
delete this.hiddenSeries[value.alias];
|
||||
});
|
||||
} else {
|
||||
// hide all but this serie
|
||||
_.each(this.seriesList, value => {
|
||||
if (value.alias === serie.alias) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hiddenSeries[value.alias] = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
toggleYAxis(info) {
|
||||
var override = _.findWhere(this.panel.seriesOverrides, { alias: info.alias });
|
||||
if (!override) {
|
||||
override = { alias: info.alias };
|
||||
this.panel.seriesOverrides.push(override);
|
||||
}
|
||||
override.yaxis = info.yaxis === 2 ? 1 : 2;
|
||||
this.render();
|
||||
};
|
||||
|
||||
addSeriesOverride(override) {
|
||||
this.panel.seriesOverrides.push(override || {});
|
||||
}
|
||||
|
||||
removeSeriesOverride(override) {
|
||||
this.panel.seriesOverrides = _.without(this.panel.seriesOverrides, override);
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Called from panel menu
|
||||
toggleLegend() {
|
||||
this.panel.legend.show = !this.panel.legend.show;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
legendValuesOptionChanged() {
|
||||
var legend = this.panel.legend;
|
||||
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
|
||||
this.render();
|
||||
}
|
||||
|
||||
exportCsv() {
|
||||
fileExport.exportSeriesListToCsv(this.seriesList);
|
||||
}
|
||||
}
|
||||
|
||||
export {GraphCtrl}
|
||||
@@ -6,6 +6,8 @@ function ($) {
|
||||
|
||||
function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
|
||||
var self = this;
|
||||
var ctrl = scope.ctrl;
|
||||
var panel = ctrl.panel;
|
||||
|
||||
var $tooltip = $('<div id="tooltip">');
|
||||
|
||||
@@ -47,12 +49,12 @@ function ($) {
|
||||
for (i = 0; i < seriesList.length; i++) {
|
||||
series = seriesList[i];
|
||||
|
||||
if (!series.data.length || (scope.panel.legend.hideEmpty && series.allIsNull)) {
|
||||
if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) {
|
||||
results.push({ hidden: true });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!series.data.length || (scope.panel.legend.hideZero && series.allIsZero)) {
|
||||
if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) {
|
||||
results.push({ hidden: true });
|
||||
continue;
|
||||
}
|
||||
@@ -61,7 +63,7 @@ function ($) {
|
||||
results.time = series.data[hoverIndex][0];
|
||||
|
||||
if (series.stack) {
|
||||
if (scope.panel.tooltip.value_type === 'individual') {
|
||||
if (panel.tooltip.value_type === 'individual') {
|
||||
value = series.data[hoverIndex][1];
|
||||
} else if (!series.stack) {
|
||||
value = series.data[hoverIndex][1];
|
||||
@@ -89,7 +91,7 @@ function ($) {
|
||||
};
|
||||
|
||||
elem.mouseleave(function () {
|
||||
if (scope.panel.tooltip.shared) {
|
||||
if (panel.tooltip.shared) {
|
||||
var plot = elem.data().plot;
|
||||
if (plot) {
|
||||
$tooltip.detach();
|
||||
@@ -98,7 +100,7 @@ function ($) {
|
||||
}
|
||||
|
||||
if (dashboard.sharedCrosshair) {
|
||||
scope.appEvent('clearCrosshair');
|
||||
ctrl.publishAppEvent('clearCrosshair');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -108,15 +110,15 @@ function ($) {
|
||||
var seriesList = getSeriesFn();
|
||||
var group, value, absoluteTime, relativeTime, hoverInfo, i, series, seriesHtml;
|
||||
|
||||
if(dashboard.sharedCrosshair){
|
||||
scope.appEvent('setCrosshair', { pos: pos, scope: scope });
|
||||
if (dashboard.sharedCrosshair) {
|
||||
ctrl.publishAppEvent('setCrosshair', { pos: pos, scope: scope });
|
||||
}
|
||||
|
||||
if (seriesList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope.panel.tooltip.shared) {
|
||||
if (panel.tooltip.shared) {
|
||||
plot.unhighlight();
|
||||
|
||||
var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos);
|
||||
@@ -151,7 +153,7 @@ function ($) {
|
||||
group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
|
||||
group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.label + ':</div>';
|
||||
|
||||
if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') {
|
||||
if (panel.stack && panel.tooltip.value_type === 'individual') {
|
||||
value = item.datapoint[1] - item.datapoint[2];
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
function (angular, _, $) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.graph');
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
module.directive('graphLegend', function(popoverSrv) {
|
||||
|
||||
@@ -16,13 +16,14 @@ function (angular, _, $) {
|
||||
link: function(scope, elem) {
|
||||
var $container = $('<section class="graph-legend"></section>');
|
||||
var firstRender = true;
|
||||
var panel = scope.panel;
|
||||
var ctrl = scope.ctrl;
|
||||
var panel = ctrl.panel;
|
||||
var data;
|
||||
var seriesList;
|
||||
var i;
|
||||
|
||||
scope.$on('render', function() {
|
||||
data = scope.seriesList;
|
||||
data = ctrl.seriesList;
|
||||
if (data) {
|
||||
render();
|
||||
}
|
||||
@@ -54,7 +55,7 @@ function (angular, _, $) {
|
||||
var el = $(e.currentTarget);
|
||||
var index = getSeriesIndexForElement(el);
|
||||
var seriesInfo = seriesList[index];
|
||||
scope.toggleSeries(seriesInfo, e);
|
||||
ctrl.toggleSeries(seriesInfo, e);
|
||||
}
|
||||
|
||||
function sortLegend(e) {
|
||||
@@ -148,7 +149,7 @@ function (angular, _, $) {
|
||||
|
||||
var html = '<div class="graph-legend-series';
|
||||
if (series.yaxis === 2) { html += ' pull-right'; }
|
||||
if (scope.hiddenSeries[series.alias]) { html += ' graph-legend-series-hidden'; }
|
||||
if (ctrl.hiddenSeries[series.alias]) { html += ' graph-legend-series-hidden'; }
|
||||
html += '" data-series-index="' + i + '">';
|
||||
html += '<div class="graph-legend-icon">';
|
||||
html += '<i class="fa fa-minus pointer" style="color:' + series.color + '"></i>';
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
<div class="editor-row small" style="padding-bottom: 0;">
|
||||
<label>Axis:</label>
|
||||
<button ng-click="toggleYAxis(series);dismiss();"
|
||||
<button ng-click="ctrl.toggleYAxis(series);dismiss();"
|
||||
class="btn btn-mini"
|
||||
ng-class="{'btn-success': series.yaxis === 1 }">
|
||||
Left
|
||||
</button>
|
||||
<button ng-click="toggleYAxis(series);dismiss();"
|
||||
<button ng-click="ctrl.toggleYAxis(series);dismiss();"
|
||||
class="btn btn-mini"
|
||||
ng-class="{'btn-success': series.yaxis === 2 }">
|
||||
Right
|
||||
@@ -16,10 +16,10 @@
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<i ng-repeat="color in colors" class="pointer"
|
||||
<i ng-repeat="color in ctrl.colors" class="pointer"
|
||||
ng-class="{'fa fa-circle-o': color === series.color,'fa fa-circle': color !== series.color}"
|
||||
ng-style="{color:color}"
|
||||
ng-click="changeSeriesColor(series, color);dismiss();"> </i>
|
||||
ng-click="ctrl.changeSeriesColor(series, color);dismiss();"> </i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
3
public/app/plugins/panel/graph/module.d.ts
vendored
3
public/app/plugins/panel/graph/module.d.ts
vendored
@@ -1,3 +0,0 @@
|
||||
declare var panel: any;
|
||||
declare var GraphCtrl: any;
|
||||
export {panel, GraphCtrl};
|
||||
@@ -1,25 +1,22 @@
|
||||
<grafana-panel>
|
||||
|
||||
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': panel.legend.rightSide}">
|
||||
<div class="graph-canvas-wrapper">
|
||||
|
||||
<div ng-if="datapointsWarning" class="datapoints-warning">
|
||||
<span class="small" ng-show="!datapointsCount">
|
||||
No datapoints <tip>No datapoints returned from metric query</tip>
|
||||
</span>
|
||||
<span class="small" ng-show="datapointsOutside">Datapoints outside time range <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
|
||||
</div>
|
||||
|
||||
<div grafana-graph class="histogram-chart" ng-dblclick="zoomOut()">
|
||||
</div>
|
||||
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': ctrl.panel.legend.rightSide}">
|
||||
<div class="graph-canvas-wrapper">
|
||||
|
||||
<div ng-if="datapointsWarning" class="datapoints-warning">
|
||||
<span class="small" ng-show="!datapointsCount">
|
||||
No datapoints <tip>No datapoints returned from metric query</tip>
|
||||
</span>
|
||||
<span class="small" ng-show="datapointsOutside">Datapoints outside time range <tip>Can be caused by timezone mismatch between browser and graphite server</tip></span>
|
||||
</div>
|
||||
|
||||
<div grafana-graph class="histogram-chart" ng-dblclick="ctrl.zoomOut()">
|
||||
</div>
|
||||
|
||||
<div class="graph-legend-wrapper" ng-if="panel.legend.show" graph-legend></div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="graph-legend-wrapper" ng-if="ctrl.panel.legend.show" graph-legend></div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
</grafana-panel>
|
||||
|
||||
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'moment',
|
||||
'app/core/utils/kbn',
|
||||
'app/core/utils/file_export',
|
||||
'app/core/time_series',
|
||||
'app/features/panel/panel_meta',
|
||||
'./seriesOverridesCtrl',
|
||||
'./graph',
|
||||
'./legend',
|
||||
],
|
||||
function (angular, _, moment, kbn, fileExport, TimeSeries, PanelMeta) {
|
||||
'use strict';
|
||||
|
||||
/** @ngInject */
|
||||
function GraphCtrl($scope, $rootScope, panelSrv, annotationsSrv, panelHelper) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Graph',
|
||||
editIcon: "fa fa-bar-chart",
|
||||
fullscreen: true,
|
||||
metricsEditor: true,
|
||||
});
|
||||
|
||||
$scope.panelMeta.addEditorTab('Axes & Grid', 'app/plugins/panel/graph/axisEditor.html');
|
||||
$scope.panelMeta.addEditorTab('Display Styles', 'app/plugins/panel/graph/styleEditor.html');
|
||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||
|
||||
$scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
|
||||
$scope.panelMeta.addExtendedMenuItem('Toggle legend', '', 'toggleLegend()');
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
// datasource name, null = default datasource
|
||||
datasource: null,
|
||||
// sets client side (flot) or native graphite png renderer (png)
|
||||
renderer: 'flot',
|
||||
// Show/hide the x-axis
|
||||
'x-axis' : true,
|
||||
// Show/hide y-axis
|
||||
'y-axis' : true,
|
||||
// y axis formats, [left axis,right axis]
|
||||
y_formats : ['short', 'short'],
|
||||
// grid options
|
||||
grid : {
|
||||
leftLogBase: 1,
|
||||
leftMax: null,
|
||||
rightMax: null,
|
||||
leftMin: null,
|
||||
rightMin: null,
|
||||
rightLogBase: 1,
|
||||
threshold1: null,
|
||||
threshold2: null,
|
||||
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
||||
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
||||
},
|
||||
// show/hide lines
|
||||
lines : true,
|
||||
// fill factor
|
||||
fill : 1,
|
||||
// line width in pixels
|
||||
linewidth : 2,
|
||||
// show hide points
|
||||
points : false,
|
||||
// point radius in pixels
|
||||
pointradius : 5,
|
||||
// show hide bars
|
||||
bars : false,
|
||||
// enable/disable stacking
|
||||
stack : false,
|
||||
// stack percentage mode
|
||||
percentage : false,
|
||||
// legend options
|
||||
legend: {
|
||||
show: true, // disable/enable legend
|
||||
values: false, // disable/enable legend values
|
||||
min: false,
|
||||
max: false,
|
||||
current: false,
|
||||
total: false,
|
||||
avg: false
|
||||
},
|
||||
// how null points should be handled
|
||||
nullPointMode : 'connected',
|
||||
// staircase line mode
|
||||
steppedLine: false,
|
||||
// tooltip options
|
||||
tooltip : {
|
||||
value_type: 'cumulative',
|
||||
shared: true,
|
||||
},
|
||||
// time overrides
|
||||
timeFrom: null,
|
||||
timeShift: null,
|
||||
// metric queries
|
||||
targets: [{}],
|
||||
// series color overrides
|
||||
aliasColors: {},
|
||||
// other style overrides
|
||||
seriesOverrides: [],
|
||||
};
|
||||
|
||||
_.defaults($scope.panel,_d);
|
||||
_.defaults($scope.panel.tooltip, _d.tooltip);
|
||||
_.defaults($scope.panel.annotate, _d.annotate);
|
||||
_.defaults($scope.panel.grid, _d.grid);
|
||||
_.defaults($scope.panel.legend, _d.legend);
|
||||
|
||||
$scope.logScales = {'linear': 1, 'log (base 2)': 2, 'log (base 10)': 10, 'log (base 32)': 32, 'log (base 1024)': 1024};
|
||||
|
||||
$scope.hiddenSeries = {};
|
||||
$scope.seriesList = [];
|
||||
$scope.unitFormats = kbn.getUnitFormats();
|
||||
|
||||
$scope.setUnitFormat = function(axis, subItem) {
|
||||
$scope.panel.y_formats[axis] = subItem.value;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.refreshData = function(datasource) {
|
||||
panelHelper.updateTimeRange($scope);
|
||||
|
||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.dashboard);
|
||||
|
||||
return panelHelper.issueMetricQuery($scope, datasource)
|
||||
.then($scope.dataHandler, function(err) {
|
||||
$scope.seriesList = [];
|
||||
$scope.render([]);
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.zoomOut = function(evt) {
|
||||
$scope.appEvent('zoom-out', evt);
|
||||
};
|
||||
|
||||
$scope.loadSnapshot = function(snapshotData) {
|
||||
panelHelper.updateTimeRange($scope);
|
||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.dashboard);
|
||||
$scope.dataHandler(snapshotData);
|
||||
};
|
||||
|
||||
$scope.dataHandler = function(results) {
|
||||
// png renderer returns just a url
|
||||
if (_.isString(results)) {
|
||||
$scope.render(results);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.datapointsWarning = false;
|
||||
$scope.datapointsCount = 0;
|
||||
$scope.datapointsOutside = false;
|
||||
|
||||
$scope.seriesList = _.map(results.data, $scope.seriesHandler);
|
||||
|
||||
$scope.datapointsWarning = $scope.datapointsCount === 0 || $scope.datapointsOutside;
|
||||
|
||||
$scope.annotationsPromise
|
||||
.then(function(annotations) {
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.seriesList.annotations = annotations;
|
||||
$scope.render($scope.seriesList);
|
||||
}, function() {
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.render($scope.seriesList);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.seriesHandler = function(seriesData, index) {
|
||||
var datapoints = seriesData.datapoints;
|
||||
var alias = seriesData.target;
|
||||
var colorIndex = index % $rootScope.colors.length;
|
||||
var color = $scope.panel.aliasColors[alias] || $rootScope.colors[colorIndex];
|
||||
|
||||
var series = new TimeSeries({
|
||||
datapoints: datapoints,
|
||||
alias: alias,
|
||||
color: color,
|
||||
});
|
||||
|
||||
if (datapoints && datapoints.length > 0) {
|
||||
var last = moment.utc(datapoints[datapoints.length - 1][1]);
|
||||
var from = moment.utc($scope.range.from);
|
||||
if (last - from < -10000) {
|
||||
$scope.datapointsOutside = true;
|
||||
}
|
||||
|
||||
$scope.datapointsCount += datapoints.length;
|
||||
}
|
||||
|
||||
return series;
|
||||
};
|
||||
|
||||
$scope.render = function(data) {
|
||||
panelHelper.broadcastRender($scope, data);
|
||||
};
|
||||
|
||||
$scope.changeSeriesColor = function(series, color) {
|
||||
series.color = color;
|
||||
$scope.panel.aliasColors[series.alias] = series.color;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.toggleSeries = function(serie, event) {
|
||||
if (event.ctrlKey || event.metaKey || event.shiftKey) {
|
||||
if ($scope.hiddenSeries[serie.alias]) {
|
||||
delete $scope.hiddenSeries[serie.alias];
|
||||
}
|
||||
else {
|
||||
$scope.hiddenSeries[serie.alias] = true;
|
||||
}
|
||||
} else {
|
||||
$scope.toggleSeriesExclusiveMode(serie);
|
||||
}
|
||||
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.toggleSeriesExclusiveMode = function(serie) {
|
||||
var hidden = $scope.hiddenSeries;
|
||||
|
||||
if (hidden[serie.alias]) {
|
||||
delete hidden[serie.alias];
|
||||
}
|
||||
|
||||
// check if every other series is hidden
|
||||
var alreadyExclusive = _.every($scope.seriesList, function(value) {
|
||||
if (value.alias === serie.alias) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hidden[value.alias];
|
||||
});
|
||||
|
||||
if (alreadyExclusive) {
|
||||
// remove all hidden series
|
||||
_.each($scope.seriesList, function(value) {
|
||||
delete $scope.hiddenSeries[value.alias];
|
||||
});
|
||||
}
|
||||
else {
|
||||
// hide all but this serie
|
||||
_.each($scope.seriesList, function(value) {
|
||||
if (value.alias === serie.alias) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.hiddenSeries[value.alias] = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleYAxis = function(info) {
|
||||
var override = _.findWhere($scope.panel.seriesOverrides, { alias: info.alias });
|
||||
if (!override) {
|
||||
override = { alias: info.alias };
|
||||
$scope.panel.seriesOverrides.push(override);
|
||||
}
|
||||
override.yaxis = info.yaxis === 2 ? 1 : 2;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.addSeriesOverride = function(override) {
|
||||
$scope.panel.seriesOverrides.push(override || {});
|
||||
};
|
||||
|
||||
$scope.removeSeriesOverride = function(override) {
|
||||
$scope.panel.seriesOverrides = _.without($scope.panel.seriesOverrides, override);
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
// Called from panel menu
|
||||
$scope.toggleLegend = function() {
|
||||
$scope.panel.legend.show = !$scope.panel.legend.show;
|
||||
$scope.get_data();
|
||||
};
|
||||
|
||||
$scope.legendValuesOptionChanged = function() {
|
||||
var legend = $scope.panel.legend;
|
||||
legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.exportCsv = function() {
|
||||
fileExport.exportSeriesListToCsv($scope.seriesList);
|
||||
};
|
||||
|
||||
panelSrv.init($scope);
|
||||
}
|
||||
|
||||
function graphPanelDirective() {
|
||||
return {
|
||||
controller: GraphCtrl,
|
||||
templateUrl: 'app/plugins/panel/graph/module.html',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
GraphCtrl: GraphCtrl,
|
||||
panel: graphPanelDirective,
|
||||
};
|
||||
});
|
||||
17
public/app/plugins/panel/graph/module.ts
Normal file
17
public/app/plugins/panel/graph/module.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
import {PanelDirective} from '../../../features/panel/panel';
|
||||
import {GraphCtrl} from './graph_ctrl';
|
||||
|
||||
import './graph';
|
||||
import './legend';
|
||||
import './seriesOverridesCtrl';
|
||||
|
||||
class GraphPanel extends PanelDirective {
|
||||
controller = GraphCtrl;
|
||||
templateUrl = 'public/app/plugins/panel/graph/module.html';
|
||||
}
|
||||
|
||||
export {
|
||||
GraphPanel,
|
||||
GraphPanel as Panel
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'app/app',
|
||||
'lodash',
|
||||
], function(angular, jquery, app, _) {
|
||||
], function(angular, jquery, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.graph', []);
|
||||
app.useModule(module);
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('SeriesOverridesCtrl', function($scope, $element, popoverSrv) {
|
||||
$scope.overrideMenu = [];
|
||||
@@ -45,18 +43,19 @@ define([
|
||||
}
|
||||
|
||||
$scope.updateCurrentOverrides();
|
||||
$scope.render();
|
||||
$scope.ctrl.render();
|
||||
};
|
||||
|
||||
$scope.colorSelected = function(color) {
|
||||
$scope.override['color'] = color;
|
||||
$scope.updateCurrentOverrides();
|
||||
$scope.render();
|
||||
$scope.ctrl.render();
|
||||
};
|
||||
|
||||
$scope.openColorSelector = function() {
|
||||
var popoverScope = $scope.$new();
|
||||
popoverScope.colorSelected = $scope.colorSelected;
|
||||
popoverScope.colors = $scope.ctrl.colors;
|
||||
|
||||
popoverSrv.show({
|
||||
element: $element.find(".dropdown"),
|
||||
@@ -69,11 +68,11 @@ define([
|
||||
$scope.removeOverride = function(option) {
|
||||
delete $scope.override[option.propertyName];
|
||||
$scope.updateCurrentOverrides();
|
||||
$scope.render();
|
||||
$scope.ctrl.render();
|
||||
};
|
||||
|
||||
$scope.getSeriesNames = function() {
|
||||
return _.map($scope.seriesList, function(series) {
|
||||
return _.map($scope.ctrl.seriesList, function(series) {
|
||||
return series.alias;
|
||||
});
|
||||
};
|
||||
@@ -107,7 +106,5 @@ define([
|
||||
$scope.addOverrideOption('Transform', 'transform', ['negative-Y']);
|
||||
$scope.addOverrideOption('Legend', 'legend', [true, false]);
|
||||
$scope.updateCurrentOverrides();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
|
||||
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
|
||||
|
||||
import 'app/features/panel/panel_srv';
|
||||
import 'app/features/panel/panel_helper';
|
||||
|
||||
import angular from 'angular';
|
||||
import {GraphCtrl} from '../module';
|
||||
import {GraphCtrl} from '../graph_ctrl';
|
||||
import helpers from '../../../../../test/specs/helpers';
|
||||
|
||||
angular.module('grafana.controllers').controller('GraphCtrl', GraphCtrl);
|
||||
|
||||
describe('GraphCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
|
||||
@@ -18,7 +13,7 @@ describe('GraphCtrl', function() {
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
beforeEach(ctx.createControllerPhase('GraphCtrl'));
|
||||
beforeEach(ctx.createPanelController(GraphCtrl));
|
||||
|
||||
describe('get_data with 2 series', function() {
|
||||
beforeEach(function() {
|
||||
@@ -29,25 +24,23 @@ describe('GraphCtrl', function() {
|
||||
{ target: 'test.cpu2', datapoints: [[1, 10]]}
|
||||
]
|
||||
}));
|
||||
ctx.scope.render = sinon.spy();
|
||||
ctx.scope.refreshData(ctx.datasource);
|
||||
ctx.ctrl.render = sinon.spy();
|
||||
ctx.ctrl.refreshData(ctx.datasource);
|
||||
ctx.scope.$digest();
|
||||
});
|
||||
|
||||
it('should send time series to render', function() {
|
||||
var data = ctx.scope.render.getCall(0).args[0];
|
||||
var data = ctx.ctrl.render.getCall(0).args[0];
|
||||
expect(data.length).to.be(2);
|
||||
});
|
||||
|
||||
describe('get_data failure following success', function() {
|
||||
beforeEach(function() {
|
||||
ctx.datasource.query = sinon.stub().returns(ctx.$q.reject('Datasource Error'));
|
||||
ctx.scope.refreshData(ctx.datasource);
|
||||
ctx.ctrl.refreshData(ctx.datasource);
|
||||
ctx.scope.$digest();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -24,11 +24,13 @@ describe('grafanaGraph', function() {
|
||||
}));
|
||||
|
||||
beforeEach(angularMocks.inject(function($rootScope, $compile) {
|
||||
var ctrl: any = {};
|
||||
var scope = $rootScope.$new();
|
||||
scope.ctrl = ctrl;
|
||||
var element = angular.element("<div style='width:" + elementWidth + "px' grafana-graph><div>");
|
||||
|
||||
scope.height = '200px';
|
||||
scope.panel = {
|
||||
ctrl.height = '200px';
|
||||
ctrl.panel = {
|
||||
legend: {},
|
||||
grid: { },
|
||||
y_formats: [],
|
||||
@@ -38,14 +40,14 @@ describe('grafanaGraph', function() {
|
||||
}
|
||||
};
|
||||
|
||||
scope.panelRenderingComplete = sinon.spy();
|
||||
scope.appEvent = sinon.spy();
|
||||
scope.onAppEvent = sinon.spy();
|
||||
scope.hiddenSeries = {};
|
||||
scope.dashboard = { timezone: 'browser' };
|
||||
scope.range = {
|
||||
$rootScope.onAppEvent = sinon.spy();
|
||||
ctrl.otherPanelInFullscreenMode = sinon.spy();
|
||||
ctrl.renderingCompleted = sinon.spy();
|
||||
ctrl.hiddenSeries = {};
|
||||
ctrl.dashboard = { timezone: 'browser' };
|
||||
ctrl.range = {
|
||||
from: moment([2015, 1, 1, 10]),
|
||||
to: moment([2015, 1, 1, 22])
|
||||
to: moment([2015, 1, 1, 22]),
|
||||
};
|
||||
ctx.data = [];
|
||||
ctx.data.push(new TimeSeries({
|
||||
@@ -57,7 +59,7 @@ describe('grafanaGraph', function() {
|
||||
alias: 'series2'
|
||||
}));
|
||||
|
||||
setupFunc(scope, ctx.data);
|
||||
setupFunc(ctrl, ctx.data);
|
||||
|
||||
$compile(element)(scope);
|
||||
scope.$digest();
|
||||
@@ -74,11 +76,11 @@ describe('grafanaGraph', function() {
|
||||
}
|
||||
|
||||
graphScenario('simple lines options', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.lines = true;
|
||||
scope.panel.fill = 5;
|
||||
scope.panel.linewidth = 3;
|
||||
scope.panel.steppedLine = true;
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
ctrl.panel.linewidth = 3;
|
||||
ctrl.panel.steppedLine = true;
|
||||
});
|
||||
|
||||
it('should configure plot with correct options', function() {
|
||||
@@ -90,8 +92,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('grid thresholds 100, 200', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.grid = {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.grid = {
|
||||
threshold1: 100,
|
||||
threshold1Color: "#111",
|
||||
threshold2: 200,
|
||||
@@ -110,8 +112,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('inverted grid thresholds 200, 100', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.grid = {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.grid = {
|
||||
threshold1: 200,
|
||||
threshold1Color: "#111",
|
||||
threshold2: 100,
|
||||
@@ -130,8 +132,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('grid thresholds from zero', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.grid = {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.grid = {
|
||||
threshold1: 0,
|
||||
threshold1Color: "#111",
|
||||
};
|
||||
@@ -144,8 +146,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('when logBase is log 10', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.grid = {
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.grid = {
|
||||
leftMax: null,
|
||||
rightMax: null,
|
||||
leftMin: null,
|
||||
@@ -163,8 +165,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('should use timeStep for barWidth', function(ctx) {
|
||||
ctx.setup(function(scope, data) {
|
||||
scope.panel.bars = true;
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.bars = true;
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[1,10],[2,20]],
|
||||
alias: 'series1',
|
||||
@@ -177,10 +179,10 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('series option overrides, fill & points', function(ctx) {
|
||||
ctx.setup(function(scope, data) {
|
||||
scope.panel.lines = true;
|
||||
scope.panel.fill = 5;
|
||||
scope.panel.seriesOverrides = [
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.lines = true;
|
||||
ctrl.panel.fill = 5;
|
||||
ctrl.panel.seriesOverrides = [
|
||||
{ alias: 'test', fill: 0, points: true }
|
||||
];
|
||||
|
||||
@@ -195,8 +197,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('should order series order according to zindex', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.seriesOverrides = [{ alias: 'series1', zindex: 2 }];
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.seriesOverrides = [{ alias: 'series1', zindex: 2 }];
|
||||
});
|
||||
|
||||
it('should move zindex 2 last', function() {
|
||||
@@ -206,8 +208,8 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('when series is hidden', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.hiddenSeries = {'series2': true};
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.hiddenSeries = {'series2': true};
|
||||
});
|
||||
|
||||
it('should remove datapoints and disable stack', function() {
|
||||
@@ -218,9 +220,9 @@ describe('grafanaGraph', function() {
|
||||
});
|
||||
|
||||
graphScenario('when stack and percent', function(ctx) {
|
||||
ctx.setup(function(scope) {
|
||||
scope.panel.percentage = true;
|
||||
scope.panel.stack = true;
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.panel.percentage = true;
|
||||
ctrl.panel.stack = true;
|
||||
});
|
||||
|
||||
it('should show percentage', function() {
|
||||
@@ -231,9 +233,9 @@ describe('grafanaGraph', function() {
|
||||
|
||||
graphScenario('when panel too narrow to show x-axis dates in same granularity as wide panels', function(ctx) {
|
||||
describe('and the range is less than 24 hours', function() {
|
||||
ctx.setup(function(scope) {
|
||||
scope.range.from = moment([2015, 1, 1, 10]);
|
||||
scope.range.to = moment([2015, 1, 1, 22]);
|
||||
ctx.setup(function(ctrl) {
|
||||
ctrl.range.from = moment([2015, 1, 1, 10]);
|
||||
ctrl.range.to = moment([2015, 1, 1, 22]);
|
||||
});
|
||||
|
||||
it('should format dates as hours minutes', function() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import GraphTooltip from '../graph_tooltip';
|
||||
var scope = {
|
||||
appEvent: sinon.spy(),
|
||||
onAppEvent: sinon.spy(),
|
||||
ctrl: {}
|
||||
};
|
||||
|
||||
var elem = $('<div></div>');
|
||||
@@ -15,8 +16,8 @@ var dashboard = { };
|
||||
|
||||
function describeSharedTooltip(desc, fn) {
|
||||
var ctx: any = {};
|
||||
ctx.scope = scope;
|
||||
ctx.scope.panel = {
|
||||
ctx.ctrl = scope.ctrl;
|
||||
ctx.ctrl.panel = {
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
@@ -51,9 +52,11 @@ describeSharedTooltip("steppedLine false, stack false", function(ctx) {
|
||||
it('should return 2 series', function() {
|
||||
expect(ctx.results.length).to.be(2);
|
||||
});
|
||||
|
||||
it('should add time to results array', function() {
|
||||
expect(ctx.results.time).to.be(10);
|
||||
});
|
||||
|
||||
it('should set value and hoverIndex', function() {
|
||||
expect(ctx.results[0].value).to.be(15);
|
||||
expect(ctx.results[1].value).to.be(2);
|
||||
@@ -93,7 +96,7 @@ describeSharedTooltip("steppedLine false, stack true, individual false", functio
|
||||
stack: true
|
||||
}
|
||||
];
|
||||
ctx.scope.panel.stack = true;
|
||||
ctx.ctrl.panel.stack = true;
|
||||
ctx.pos = { x: 11 };
|
||||
});
|
||||
|
||||
@@ -124,7 +127,7 @@ describeSharedTooltip("steppedLine false, stack true, individual false, series s
|
||||
stack: false
|
||||
}
|
||||
];
|
||||
ctx.scope.panel.stack = true;
|
||||
ctx.ctrl.panel.stack = true;
|
||||
ctx.pos = { x: 11 };
|
||||
});
|
||||
|
||||
@@ -156,15 +159,14 @@ describeSharedTooltip("steppedLine false, stack true, individual true", function
|
||||
stack: false
|
||||
}
|
||||
];
|
||||
ctx.scope.panel.stack = true;
|
||||
ctx.scope.panel.tooltip.value_type = 'individual';
|
||||
ctx.ctrl.panel.stack = true;
|
||||
ctx.ctrl.panel.tooltip.value_type = 'individual';
|
||||
ctx.pos = { x: 11 };
|
||||
});
|
||||
|
||||
it('should not show stacked value', function() {
|
||||
expect(ctx.results[1].value).to.be(2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Chart Options</h5>
|
||||
<editor-opt-bool text="Bars" model="panel.bars" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Lines" model="panel.lines" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Points" model="panel.points" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Bars" model="ctrl.panel.bars" change="ctrl.render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Lines" model="ctrl.panel.lines" change="ctrl.render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Points" model="ctrl.panel.points" change="ctrl.render()"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h5>Line options</h5>
|
||||
<div class="editor-option" ng-show="panel.lines">
|
||||
<div class="editor-option" ng-show="ctrl.panel.lines">
|
||||
<label class="small">Line Fill</label>
|
||||
<select class="input-mini" ng-model="panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
|
||||
<select class="input-mini" ng-model="ctrl.panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="panel.lines">
|
||||
<div class="editor-option" ng-show="ctrl.panel.lines">
|
||||
<label class="small">Line Width</label>
|
||||
<select class="input-mini" ng-model="panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
|
||||
<select class="input-mini" ng-model="ctrl.panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="panel.points">
|
||||
<div class="editor-option" ng-show="ctrl.panel.points">
|
||||
<label class="small">Point Radius</label>
|
||||
<select class="input-mini" ng-model="panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]" ng-change="render()"></select>
|
||||
<select class="input-mini" ng-model="ctrl.panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()"></select>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Null point mode<tip>Define how null values should be drawn</tip></label>
|
||||
<select class="input-medium" ng-model="panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="render()"></select>
|
||||
<select class="input-medium" ng-model="ctrl.panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="ctrl.render()"></select>
|
||||
</div>
|
||||
|
||||
<editor-opt-bool text="Staircase line" model="panel.steppedLine" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Staircase line" model="ctrl.panel.steppedLine" change="ctrl.render()"></editor-opt-bool>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h5>Multiple Series</h5>
|
||||
|
||||
<editor-opt-bool text="Stack" model="panel.stack" change="render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Percent" model="panel.percentage" change="render()" tip="Stack as a percentage of total"></editor-opt-bool>
|
||||
<editor-opt-bool text="Stack" model="ctrl.panel.stack" change="ctrl.render()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Percent" model="ctrl.panel.percentage" change="ctrl.render()" tip="Stack as a percentage of total"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h5>Rendering</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Flot <tip>client side</tip></label>
|
||||
<input type="radio" class="input-small" ng-model="panel.renderer" value="flot" ng-change="get_data()" />
|
||||
<input type="radio" class="input-small" ng-model="ctrl.panel.renderer" value="flot" ng-change="ctrl.refresh()" />
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite PNG <tip>server side</tip></label>
|
||||
<input type="radio" class="input-small" ng-model="panel.renderer" value="png" ng-change="get_data()" />
|
||||
<input type="radio" class="input-small" ng-model="ctrl.panel.renderer" value="png" ng-change="ctr.refresh()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h5>Tooltip</h5>
|
||||
<editor-opt-bool
|
||||
text="All series" model="panel.tooltip.shared" change="render()"
|
||||
text="All series" model="ctrl.panel.tooltip.shared" change="ctrl.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">
|
||||
<div class="editor-option" ng-show="ctrl.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>
|
||||
<select class="input-small" ng-model="ctrl.panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']" ng-change="ctrl.render()"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,10 +64,10 @@
|
||||
<div class="section">
|
||||
<h5>Series specific overrides <tip>Regex match example: /server[0-3]/i </tip></h5>
|
||||
<div class="tight-form-container">
|
||||
<div class="tight-form" ng-repeat="override in panel.seriesOverrides" ng-controller="SeriesOverridesCtrl">
|
||||
<div class="tight-form" ng-repeat="override in ctrl.panel.seriesOverrides" ng-controller="SeriesOverridesCtrl">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
<i class="fa fa-remove pointer" ng-click="removeSeriesOverride(override)"></i>
|
||||
<i class="fa fa-remove pointer" ng-click="ctrl.removeSeriesOverride(override)"></i>
|
||||
</li>
|
||||
|
||||
<li class="tight-form-item">
|
||||
@@ -75,7 +75,7 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="text" ng-model="override.alias" bs-typeahead="getSeriesNames" ng-blur="render()" data-min-length=0 data-items=100 class="input-medium tight-form-input" >
|
||||
<input type="text" ng-model="override.alias" bs-typeahead="getSeriesNames" ng-blur="ctrl.render()" data-min-length=0 data-items=100 class="input-medium tight-form-input" >
|
||||
</li>
|
||||
|
||||
<li class="tight-form-item" ng-repeat="option in currentOverrides">
|
||||
@@ -95,7 +95,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="addSeriesOverride()">
|
||||
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="ctrl.addSeriesOverride()">
|
||||
Add series specific option
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -3,240 +3,231 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import PanelMeta from 'app/features/panel/panel_meta2';
|
||||
import TimeSeries from '../../../core/time_series2';
|
||||
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
||||
|
||||
export class SingleStatCtrl {
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
links: [],
|
||||
datasource: null,
|
||||
maxDataPoints: 100,
|
||||
interval: null,
|
||||
targets: [{}],
|
||||
cacheTimeout: null,
|
||||
format: 'none',
|
||||
prefix: '',
|
||||
postfix: '',
|
||||
nullText: null,
|
||||
valueMaps: [
|
||||
{ value: 'null', op: '=', text: 'N/A' }
|
||||
],
|
||||
nullPointMode: 'connected',
|
||||
valueName: 'avg',
|
||||
prefixFontSize: '50%',
|
||||
valueFontSize: '80%',
|
||||
postfixFontSize: '50%',
|
||||
thresholds: '',
|
||||
colorBackground: false,
|
||||
colorValue: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
sparkline: {
|
||||
show: false,
|
||||
full: false,
|
||||
lineColor: 'rgb(31, 120, 193)',
|
||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||
}
|
||||
};
|
||||
|
||||
export class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
series: any[];
|
||||
data: any[];
|
||||
fontSizes: any[];
|
||||
unitFormats: any[];
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, panelSrv, panelHelper) {
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Singlestat',
|
||||
editIcon: "fa fa-dashboard",
|
||||
fullscreen: true,
|
||||
metricsEditor: true
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
}
|
||||
|
||||
|
||||
initEditMode() {
|
||||
super.initEditMode();
|
||||
this.icon = "fa fa-dashboard";
|
||||
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
||||
this.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html', 2);
|
||||
this.unitFormats = kbn.getUnitFormats();
|
||||
}
|
||||
|
||||
setUnitFormat(subItem) {
|
||||
this.panel.format = subItem.value;
|
||||
this.render();
|
||||
}
|
||||
|
||||
refreshData(datasource) {
|
||||
return this.issueQueries(datasource)
|
||||
.then(this.dataHandler.bind(this))
|
||||
.catch(err => {
|
||||
this.series = [];
|
||||
this.render();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
loadSnapshot(snapshotData) {
|
||||
this.updateTimeRange();
|
||||
this.dataHandler(snapshotData);
|
||||
}
|
||||
|
||||
dataHandler(results) {
|
||||
this.series = _.map(results.data, this.seriesHandler.bind(this));
|
||||
this.render();
|
||||
}
|
||||
|
||||
seriesHandler(seriesData) {
|
||||
var series = new TimeSeries({
|
||||
datapoints: seriesData.datapoints,
|
||||
alias: seriesData.target,
|
||||
});
|
||||
|
||||
$scope.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
|
||||
series.flotpairs = series.getFlotPairs(this.panel.nullPointMode);
|
||||
return series;
|
||||
}
|
||||
|
||||
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/singlestat/editor.html');
|
||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||
setColoring(options) {
|
||||
if (options.background) {
|
||||
this.panel.colorValue = false;
|
||||
this.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
||||
} else {
|
||||
this.panel.colorBackground = false;
|
||||
this.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
links: [],
|
||||
datasource: null,
|
||||
maxDataPoints: 100,
|
||||
interval: null,
|
||||
targets: [{}],
|
||||
cacheTimeout: null,
|
||||
format: 'none',
|
||||
prefix: '',
|
||||
postfix: '',
|
||||
nullText: null,
|
||||
valueMaps: [
|
||||
{ value: 'null', op: '=', text: 'N/A' }
|
||||
],
|
||||
nullPointMode: 'connected',
|
||||
valueName: 'avg',
|
||||
prefixFontSize: '50%',
|
||||
valueFontSize: '80%',
|
||||
postfixFontSize: '50%',
|
||||
thresholds: '',
|
||||
colorBackground: false,
|
||||
colorValue: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
sparkline: {
|
||||
show: false,
|
||||
full: false,
|
||||
lineColor: 'rgb(31, 120, 193)',
|
||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||
invertColorOrder() {
|
||||
var tmp = this.panel.colors[0];
|
||||
this.panel.colors[0] = this.panel.colors[2];
|
||||
this.panel.colors[2] = tmp;
|
||||
this.render();
|
||||
}
|
||||
|
||||
getDecimalsForValue(value) {
|
||||
if (_.isNumber(this.panel.decimals)) {
|
||||
return {decimals: this.panel.decimals, scaledDecimals: null};
|
||||
}
|
||||
|
||||
var delta = value / 2;
|
||||
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||
|
||||
var magn = Math.pow(10, -dec),
|
||||
norm = delta / magn, // norm is between 1.0 and 10.0
|
||||
size;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
// special case for 2.5, requires an extra decimal
|
||||
if (norm > 2.25) {
|
||||
size = 2.5;
|
||||
++dec;
|
||||
}
|
||||
};
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
_.defaults($scope.panel, _d);
|
||||
size *= magn;
|
||||
|
||||
$scope.unitFormats = kbn.getUnitFormats();
|
||||
// reduce starting decimals if not needed
|
||||
if (Math.floor(value) === value) { dec = 0; }
|
||||
|
||||
$scope.setUnitFormat = function(subItem) {
|
||||
$scope.panel.format = subItem.value;
|
||||
$scope.render();
|
||||
};
|
||||
var result: any = {};
|
||||
result.decimals = Math.max(0, dec);
|
||||
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
|
||||
|
||||
$scope.init = function() {
|
||||
panelSrv.init($scope);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.refreshData = function(datasource) {
|
||||
panelHelper.updateTimeRange($scope);
|
||||
render() {
|
||||
var data: any = {};
|
||||
this.setValues(data);
|
||||
|
||||
return panelHelper.issueMetricQuery($scope, datasource)
|
||||
.then($scope.dataHandler, function(err) {
|
||||
$scope.series = [];
|
||||
$scope.render();
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
|
||||
return Number(strVale.trim());
|
||||
});
|
||||
|
||||
$scope.loadSnapshot = function(snapshotData) {
|
||||
panelHelper.updateTimeRange($scope);
|
||||
$scope.dataHandler(snapshotData);
|
||||
};
|
||||
data.colorMap = this.panel.colors;
|
||||
|
||||
$scope.dataHandler = function(results) {
|
||||
$scope.series = _.map(results.data, $scope.seriesHandler);
|
||||
$scope.render();
|
||||
};
|
||||
this.data = data;
|
||||
this.broadcastRender();
|
||||
}
|
||||
|
||||
$scope.seriesHandler = function(seriesData) {
|
||||
var series = new TimeSeries({
|
||||
datapoints: seriesData.datapoints,
|
||||
alias: seriesData.target,
|
||||
});
|
||||
setValues(data) {
|
||||
data.flotpairs = [];
|
||||
|
||||
series.flotpairs = series.getFlotPairs($scope.panel.nullPointMode);
|
||||
if (this.series.length > 1) {
|
||||
this.inspector.error = new Error();
|
||||
this.inspector.error.message = 'Multiple Series Error';
|
||||
this.inspector.error.data = 'Metric query returns ' + this.series.length +
|
||||
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify(this.series);
|
||||
throw this.inspector.error;
|
||||
}
|
||||
|
||||
return series;
|
||||
};
|
||||
if (this.series && this.series.length > 0) {
|
||||
var lastPoint = _.last(this.series[0].datapoints);
|
||||
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
||||
|
||||
$scope.setColoring = function(options) {
|
||||
if (options.background) {
|
||||
$scope.panel.colorValue = false;
|
||||
$scope.panel.colors = ['rgba(71, 212, 59, 0.4)', 'rgba(245, 150, 40, 0.73)', 'rgba(225, 40, 40, 0.59)'];
|
||||
if (_.isString(lastValue)) {
|
||||
data.value = 0;
|
||||
data.valueFormated = lastValue;
|
||||
data.valueRounded = 0;
|
||||
} else {
|
||||
$scope.panel.colorBackground = false;
|
||||
$scope.panel.colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||
data.value = this.series[0].stats[this.panel.valueName];
|
||||
data.flotpairs = this.series[0].flotpairs;
|
||||
|
||||
var decimalInfo = this.getDecimalsForValue(data.value);
|
||||
var formatFunc = kbn.valueFormats[this.panel.format];
|
||||
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
|
||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||
}
|
||||
$scope.render();
|
||||
};
|
||||
}
|
||||
|
||||
$scope.invertColorOrder = function() {
|
||||
var tmp = $scope.panel.colors[0];
|
||||
$scope.panel.colors[0] = $scope.panel.colors[2];
|
||||
$scope.panel.colors[2] = tmp;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.getDecimalsForValue = function(value) {
|
||||
if (_.isNumber($scope.panel.decimals)) {
|
||||
return { decimals: $scope.panel.decimals, scaledDecimals: null };
|
||||
}
|
||||
|
||||
var delta = value / 2;
|
||||
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||
|
||||
var magn = Math.pow(10, -dec),
|
||||
norm = delta / magn, // norm is between 1.0 and 10.0
|
||||
size;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
// special case for 2.5, requires an extra decimal
|
||||
if (norm > 2.25) {
|
||||
size = 2.5;
|
||||
++dec;
|
||||
}
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
|
||||
// reduce starting decimals if not needed
|
||||
if (Math.floor(value) === value) { dec = 0; }
|
||||
|
||||
var result: any = {};
|
||||
result.decimals = Math.max(0, dec);
|
||||
result.scaledDecimals = result.decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
$scope.render = function() {
|
||||
var data: any = {};
|
||||
|
||||
$scope.setValues(data);
|
||||
|
||||
data.thresholds = $scope.panel.thresholds.split(',').map(function(strVale) {
|
||||
return Number(strVale.trim());
|
||||
});
|
||||
|
||||
data.colorMap = $scope.panel.colors;
|
||||
|
||||
$scope.data = data;
|
||||
$scope.$broadcast('render');
|
||||
};
|
||||
|
||||
$scope.setValues = function(data) {
|
||||
data.flotpairs = [];
|
||||
|
||||
if ($scope.series.length > 1) {
|
||||
$scope.inspector.error = new Error();
|
||||
$scope.inspector.error.message = 'Multiple Series Error';
|
||||
$scope.inspector.error.data = 'Metric query returns ' + $scope.series.length +
|
||||
' series. Single Stat Panel expects a single series.\n\nResponse:\n'+JSON.stringify($scope.series);
|
||||
throw $scope.inspector.error;
|
||||
}
|
||||
|
||||
if ($scope.series && $scope.series.length > 0) {
|
||||
var lastPoint = _.last($scope.series[0].datapoints);
|
||||
var lastValue = _.isArray(lastPoint) ? lastPoint[0] : null;
|
||||
|
||||
if (_.isString(lastValue)) {
|
||||
data.value = 0;
|
||||
data.valueFormated = lastValue;
|
||||
data.valueRounded = 0;
|
||||
} else {
|
||||
data.value = $scope.series[0].stats[$scope.panel.valueName];
|
||||
data.flotpairs = $scope.series[0].flotpairs;
|
||||
|
||||
var decimalInfo = $scope.getDecimalsForValue(data.value);
|
||||
var formatFunc = kbn.valueFormats[$scope.panel.format];
|
||||
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
|
||||
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
|
||||
}
|
||||
}
|
||||
|
||||
// check value to text mappings
|
||||
for (var i = 0; i < $scope.panel.valueMaps.length; i++) {
|
||||
var map = $scope.panel.valueMaps[i];
|
||||
// special null case
|
||||
if (map.value === 'null') {
|
||||
if (data.value === null || data.value === void 0) {
|
||||
data.valueFormated = map.text;
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// value/number to text mapping
|
||||
var value = parseFloat(map.value);
|
||||
if (value === data.value) {
|
||||
// check value to text mappings
|
||||
for (var i = 0; i < this.panel.valueMaps.length; i++) {
|
||||
var map = this.panel.valueMaps[i];
|
||||
// special null case
|
||||
if (map.value === 'null') {
|
||||
if (data.value === null || data.value === void 0) {
|
||||
data.valueFormated = map.text;
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.value === null || data.value === void 0) {
|
||||
data.valueFormated = "no value";
|
||||
// value/number to text mapping
|
||||
var value = parseFloat(map.value);
|
||||
if (value === data.value) {
|
||||
data.valueFormated = map.text;
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.removeValueMap = function(map) {
|
||||
var index = _.indexOf($scope.panel.valueMaps, map);
|
||||
$scope.panel.valueMaps.splice(index, 1);
|
||||
$scope.render();
|
||||
};
|
||||
if (data.value === null || data.value === void 0) {
|
||||
data.valueFormated = "no value";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addValueMap = function() {
|
||||
$scope.panel.valueMaps.push({value: '', op: '=', text: '' });
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
removeValueMap(map) {
|
||||
var index = _.indexOf(this.panel.valueMaps, map);
|
||||
this.panel.valueMaps.splice(index, 1);
|
||||
this.render();
|
||||
};
|
||||
|
||||
addValueMap() {
|
||||
this.panel.valueMaps.push({value: '', op: '=', text: '' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input"
|
||||
ng-model="panel.prefix" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.prefix" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Value
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="panel.valueName" ng-options="f for f in ['min','max','avg', 'current', 'total']" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ['min','max','avg', 'current', 'total']" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Postfix
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input last"
|
||||
ng-model="panel.postfix" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.postfix" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -37,19 +37,19 @@
|
||||
Prefix
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="panel.prefixFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" ng-model="ctrl.panel.prefixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Value
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="panel.valueFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Postfix
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input last" ng-model="panel.postfixFontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input last" ng-model="ctrl.panel.postfixFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -60,16 +60,16 @@
|
||||
<strong>Unit</strong>
|
||||
</li>
|
||||
<li class="dropdown" style="width: 266px;"
|
||||
ng-model="panel.format"
|
||||
dropdown-typeahead="unitFormats"
|
||||
dropdown-typeahead-on-select="setUnitFormat($subItem)">
|
||||
ng-model="ctrl.panel.format"
|
||||
dropdown-typeahead="ctrl.unitFormats"
|
||||
dropdown-typeahead-on-select="ctrl.setUnitFormat($subItem)">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Decimals
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input last" placeholder="auto" bs-tooltip="'Override automatic decimal precision for legend and tooltips'" data-placement="right"
|
||||
ng-model="panel.decimals" ng-change="render()" ng-model-onblur>
|
||||
ng-model="ctrl.panel.decimals" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -86,32 +86,32 @@
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Background
|
||||
<input class="cr1" id="panel.colorBackground" type="checkbox"
|
||||
ng-model="panel.colorBackground" ng-checked="panel.colorBackground" ng-change="render()">
|
||||
<label for="panel.colorBackground" class="cr1"></label>
|
||||
<input class="cr1" id="ctrl.panel.colorBackground" type="checkbox"
|
||||
ng-model="ctrl.panel.colorBackground" ng-checked="ctrl.panel.colorBackground" ng-change="ctrl.render()">
|
||||
<label for="ctrl.panel.colorBackground" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Value
|
||||
<input class="cr1" id="panel.colorValue" type="checkbox"
|
||||
ng-model="panel.colorValue" ng-checked="panel.colorValue" ng-change="render()">
|
||||
<label for="panel.colorValue" class="cr1"></label>
|
||||
<input class="cr1" id="ctrl.panel.colorValue" type="checkbox"
|
||||
ng-model="ctrl.panel.colorValue" ng-checked="ctrl.panel.colorValue" ng-change="ctrl.render()">
|
||||
<label for="ctrl.panel.colorValue" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Thresholds<tip>Define two threshold values<br /> 50,80 will produce: <50 = Green, 50:80 = Yellow, >80 = Red</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-large tight-form-input" ng-model="panel.thresholds" ng-blur="render()" placeholder="50,80"></input>
|
||||
<input type="text" class="input-large tight-form-input" ng-model="ctrl.panel.thresholds" ng-blur="ctrl.render()" placeholder="50,80"></input>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Colors
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<spectrum-picker ng-model="panel.colors[0]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.colors[1]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="panel.colors[2]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.colors[0]" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.colors[1]" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.colors[2]" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" ng-click="invertColorOrder()">invert order</a>
|
||||
<a class="pointer" ng-click="ctrl.invertColorOrder()">invert order</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -128,27 +128,27 @@
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Show
|
||||
<input class="cr1" id="panel.sparkline.show" type="checkbox"
|
||||
ng-model="panel.sparkline.show" ng-checked="panel.sparkline.show" ng-change="render()">
|
||||
<label for="panel.sparkline.show" class="cr1"></label>
|
||||
<input class="cr1" id="ctrl.panel.sparkline.show" type="checkbox"
|
||||
ng-model="ctrl.panel.sparkline.show" ng-checked="ctrl.panel.sparkline.show" ng-change="ctrl.render()">
|
||||
<label for="ctrl.panel.sparkline.show" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Background mode
|
||||
<input class="cr1" id="panel.sparkline.full" type="checkbox"
|
||||
ng-model="panel.sparkline.full" ng-checked="panel.sparkline.full" ng-change="render()">
|
||||
<label for="panel.sparkline.full" class="cr1"></label>
|
||||
<input class="cr1" id="ctrl.panel.sparkline.full" type="checkbox"
|
||||
ng-model="ctrl.panel.sparkline.full" ng-checked="ctrl.panel.sparkline.full" ng-change="ctrl.render()">
|
||||
<label for="ctrl.panel.sparkline.full" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Line Color
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<spectrum-picker ng-model="panel.sparkline.lineColor" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.sparkline.lineColor" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Fill Color
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<spectrum-picker ng-model="panel.sparkline.fillColor" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="ctrl.panel.sparkline.fillColor" ng-change="ctrl.render()" ></spectrum-picker>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -163,21 +163,21 @@
|
||||
<li class="tight-form-item">
|
||||
<strong>Value to text mapping</strong>
|
||||
</li>
|
||||
<li class="tight-form-item" ng-repeat-start="map in panel.valueMaps">
|
||||
<i class="fa fa-remove pointer" ng-click="removeValueMap(map)"></i>
|
||||
<li class="tight-form-item" ng-repeat-start="map in ctrl.panel.valueMaps">
|
||||
<i class="fa fa-remove pointer" ng-click="ctrl.removeValueMap(map)"></i>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" ng-model="map.value" placeholder="value" class="input-mini tight-form-input" ng-blur="render()">
|
||||
<input type="text" ng-model="ctrl.map.value" placeholder="value" class="input-mini tight-form-input" ng-blur="ctrl.render()">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<i class="fa fa-arrow-right"></i>
|
||||
</li>
|
||||
<li ng-repeat-end>
|
||||
<input type="text" placeholder="text" ng-model="map.text" class="input-mini tight-form-input" ng-blur="render()">
|
||||
<input type="text" placeholder="text" ng-model="ctrl.map.text" class="input-mini tight-form-input" ng-blur="ctrl.render()">
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="pointer tight-form-item last" ng-click="addValueMap();">
|
||||
<a class="pointer tight-form-item last" ng-click="ctrl.addValueMap();">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<grafana-panel>
|
||||
<div class="singlestat-panel"></div>
|
||||
<div class="clearfix"></div>
|
||||
</grafana-panel>
|
||||
<div class="singlestat-panel">
|
||||
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -2,222 +2,225 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
import 'jquery.flot';
|
||||
import {SingleStatCtrl} from './controller';
|
||||
import {PanelDirective} from '../../../features/panel/panel';
|
||||
|
||||
angular.module('grafana.directives').directive('singleStatPanel', singleStatPanel);
|
||||
class SingleStatPanel extends PanelDirective {
|
||||
templateUrl = 'app/plugins/panel/singlestat/module.html';
|
||||
controller = SingleStatCtrl;
|
||||
|
||||
/** @ngInject */
|
||||
function singleStatPanel($location, linkSrv, $timeout, templateSrv) {
|
||||
'use strict';
|
||||
return {
|
||||
controller: SingleStatCtrl,
|
||||
templateUrl: 'app/plugins/panel/singlestat/module.html',
|
||||
link: function(scope, elem) {
|
||||
var data, panel, linkInfo, $panelContainer;
|
||||
var firstRender = true;
|
||||
/** @ngInject */
|
||||
constructor(private $location, private linkSrv, private $timeout, private templateSrv) {
|
||||
super();
|
||||
}
|
||||
|
||||
scope.$on('render', function() {
|
||||
if (firstRender) {
|
||||
var inner = elem.find('.singlestat-panel');
|
||||
if (inner.length) {
|
||||
elem = inner;
|
||||
$panelContainer = elem.parents('.panel-container');
|
||||
firstRender = false;
|
||||
hookupDrilldownLinkTooltip();
|
||||
}
|
||||
}
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
var $location = this.$location;
|
||||
var linkSrv = this.linkSrv;
|
||||
var $timeout = this.$timeout;
|
||||
var panel = ctrl.panel;
|
||||
var templateSrv = this.templateSrv;
|
||||
var data, linkInfo, $panelContainer;
|
||||
var firstRender = true;
|
||||
|
||||
render();
|
||||
scope.panelRenderingComplete();
|
||||
});
|
||||
|
||||
function setElementHeight() {
|
||||
try {
|
||||
var height = scope.height || panel.height || scope.row.height;
|
||||
if (_.isString(height)) {
|
||||
height = parseInt(height.replace('px', ''), 10);
|
||||
}
|
||||
|
||||
height -= 5; // padding
|
||||
height -= panel.title ? 24 : 9; // subtract panel title bar
|
||||
|
||||
elem.css('height', height + 'px');
|
||||
|
||||
return true;
|
||||
} catch (e) { // IE throws errors sometimes
|
||||
return false;
|
||||
scope.$on('render', function() {
|
||||
if (firstRender) {
|
||||
var inner = elem.find('.singlestat-panel');
|
||||
if (inner.length) {
|
||||
elem = inner;
|
||||
$panelContainer = elem.parents('.panel-container');
|
||||
firstRender = false;
|
||||
hookupDrilldownLinkTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
function applyColoringThresholds(value, valueString) {
|
||||
if (!panel.colorValue) {
|
||||
return valueString;
|
||||
render();
|
||||
ctrl.renderingCompleted();
|
||||
});
|
||||
|
||||
function setElementHeight() {
|
||||
try {
|
||||
var height = scope.height || panel.height || ctrl.row.height;
|
||||
if (_.isString(height)) {
|
||||
height = parseInt(height.replace('px', ''), 10);
|
||||
}
|
||||
|
||||
var color = getColorForValue(data, value);
|
||||
if (color) {
|
||||
return '<span style="color:' + color + '">'+ valueString + '</span>';
|
||||
}
|
||||
height -= 5; // padding
|
||||
height -= panel.title ? 24 : 9; // subtract panel title bar
|
||||
|
||||
elem.css('height', height + 'px');
|
||||
|
||||
return true;
|
||||
} catch (e) { // IE throws errors sometimes
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyColoringThresholds(value, valueString) {
|
||||
if (!panel.colorValue) {
|
||||
return valueString;
|
||||
}
|
||||
|
||||
function getSpan(className, fontSize, value) {
|
||||
value = templateSrv.replace(value);
|
||||
return '<span class="' + className + '" style="font-size:' + fontSize + '">' +
|
||||
value + '</span>';
|
||||
var color = getColorForValue(data, value);
|
||||
if (color) {
|
||||
return '<span style="color:' + color + '">'+ valueString + '</span>';
|
||||
}
|
||||
|
||||
function getBigValueHtml() {
|
||||
var body = '<div class="singlestat-panel-value-container">';
|
||||
return valueString;
|
||||
}
|
||||
|
||||
if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, scope.panel.prefix); }
|
||||
function getSpan(className, fontSize, value) {
|
||||
value = templateSrv.replace(value);
|
||||
return '<span class="' + className + '" style="font-size:' + fontSize + '">' +
|
||||
value + '</span>';
|
||||
}
|
||||
|
||||
var value = applyColoringThresholds(data.valueRounded, data.valueFormated);
|
||||
body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
|
||||
function getBigValueHtml() {
|
||||
var body = '<div class="singlestat-panel-value-container">';
|
||||
|
||||
if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
|
||||
if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); }
|
||||
|
||||
body += '</div>';
|
||||
var value = applyColoringThresholds(data.valueRounded, data.valueFormated);
|
||||
body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
|
||||
|
||||
return body;
|
||||
if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
|
||||
|
||||
body += '</div>';
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
function addSparkline() {
|
||||
var width = elem.width() + 20;
|
||||
var height = elem.height() || 100;
|
||||
|
||||
var plotCanvas = $('<div></div>');
|
||||
var plotCss: any = {};
|
||||
plotCss.position = 'absolute';
|
||||
|
||||
if (panel.sparkline.full) {
|
||||
plotCss.bottom = '5px';
|
||||
plotCss.left = '-5px';
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
var dynamicHeightMargin = height <= 100 ? 5 : (Math.round((height/100)) * 15) + 5;
|
||||
plotCss.height = (height - dynamicHeightMargin) + 'px';
|
||||
} else {
|
||||
plotCss.bottom = "0px";
|
||||
plotCss.left = "-5px";
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
plotCss.height = Math.floor(height * 0.25) + "px";
|
||||
}
|
||||
|
||||
function addSparkline() {
|
||||
var panel = scope.panel;
|
||||
var width = elem.width() + 20;
|
||||
var height = elem.height() || 100;
|
||||
plotCanvas.css(plotCss);
|
||||
|
||||
var plotCanvas = $('<div></div>');
|
||||
var plotCss: any = {};
|
||||
plotCss.position = 'absolute';
|
||||
|
||||
if (panel.sparkline.full) {
|
||||
plotCss.bottom = '5px';
|
||||
plotCss.left = '-5px';
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
var dynamicHeightMargin = height <= 100 ? 5 : (Math.round((height/100)) * 15) + 5;
|
||||
plotCss.height = (height - dynamicHeightMargin) + 'px';
|
||||
} else {
|
||||
plotCss.bottom = "0px";
|
||||
plotCss.left = "-5px";
|
||||
plotCss.width = (width - 10) + 'px';
|
||||
plotCss.height = Math.floor(height * 0.25) + "px";
|
||||
}
|
||||
|
||||
plotCanvas.css(plotCss);
|
||||
|
||||
var options = {
|
||||
legend: { show: false },
|
||||
series: {
|
||||
lines: {
|
||||
show: true,
|
||||
fill: 1,
|
||||
lineWidth: 1,
|
||||
fillColor: panel.sparkline.fillColor,
|
||||
},
|
||||
var options = {
|
||||
legend: { show: false },
|
||||
series: {
|
||||
lines: {
|
||||
show: true,
|
||||
fill: 1,
|
||||
lineWidth: 1,
|
||||
fillColor: panel.sparkline.fillColor,
|
||||
},
|
||||
yaxes: { show: false },
|
||||
xaxis: {
|
||||
show: false,
|
||||
mode: "time",
|
||||
min: scope.range.from.valueOf(),
|
||||
max: scope.range.to.valueOf(),
|
||||
},
|
||||
grid: { hoverable: false, show: false },
|
||||
};
|
||||
},
|
||||
yaxes: { show: false },
|
||||
xaxis: {
|
||||
show: false,
|
||||
mode: "time",
|
||||
min: ctrl.range.from.valueOf(),
|
||||
max: ctrl.range.to.valueOf(),
|
||||
},
|
||||
grid: { hoverable: false, show: false },
|
||||
};
|
||||
|
||||
elem.append(plotCanvas);
|
||||
elem.append(plotCanvas);
|
||||
|
||||
var plotSeries = {
|
||||
data: data.flotpairs,
|
||||
color: panel.sparkline.lineColor
|
||||
};
|
||||
var plotSeries = {
|
||||
data: data.flotpairs,
|
||||
color: panel.sparkline.lineColor
|
||||
};
|
||||
|
||||
$.plot(plotCanvas, [plotSeries], options);
|
||||
}
|
||||
$.plot(plotCanvas, [plotSeries], options);
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!scope.data) { return; }
|
||||
function render() {
|
||||
if (!ctrl.data) { return; }
|
||||
|
||||
data = scope.data;
|
||||
panel = scope.panel;
|
||||
data = ctrl.data;
|
||||
setElementHeight();
|
||||
|
||||
setElementHeight();
|
||||
var body = getBigValueHtml();
|
||||
|
||||
var body = getBigValueHtml();
|
||||
|
||||
if (panel.colorBackground && !isNaN(data.valueRounded)) {
|
||||
var color = getColorForValue(data, data.valueRounded);
|
||||
if (color) {
|
||||
$panelContainer.css('background-color', color);
|
||||
if (scope.fullscreen) {
|
||||
elem.css('background-color', color);
|
||||
} else {
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$panelContainer.css('background-color', '');
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
|
||||
elem.html(body);
|
||||
|
||||
if (panel.sparkline.show) {
|
||||
addSparkline();
|
||||
}
|
||||
|
||||
elem.toggleClass('pointer', panel.links.length > 0);
|
||||
|
||||
if (panel.links.length > 0) {
|
||||
linkInfo = linkSrv.getPanelLinkAnchorInfo(panel.links[0], scope.panel.scopedVars);
|
||||
} else {
|
||||
linkInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
function hookupDrilldownLinkTooltip() {
|
||||
// drilldown link tooltip
|
||||
var drilldownTooltip = $('<div id="tooltip" class="">hello</div>"');
|
||||
|
||||
elem.mouseleave(function() {
|
||||
if (panel.links.length === 0) { return;}
|
||||
drilldownTooltip.detach();
|
||||
});
|
||||
|
||||
elem.click(function(evt) {
|
||||
if (!linkInfo) { return; }
|
||||
// ignore title clicks in title
|
||||
if ($(evt).parents('.panel-header').length > 0) { return; }
|
||||
|
||||
if (linkInfo.target === '_blank') {
|
||||
var redirectWindow = window.open(linkInfo.href, '_blank');
|
||||
redirectWindow.location;
|
||||
return;
|
||||
}
|
||||
|
||||
if (linkInfo.href.indexOf('http') === 0) {
|
||||
window.location.href = linkInfo.href;
|
||||
if (panel.colorBackground && !isNaN(data.valueRounded)) {
|
||||
var color = getColorForValue(data, data.valueRounded);
|
||||
if (color) {
|
||||
$panelContainer.css('background-color', color);
|
||||
if (scope.fullscreen) {
|
||||
elem.css('background-color', color);
|
||||
} else {
|
||||
$timeout(function() {
|
||||
$location.url(linkInfo.href);
|
||||
});
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$panelContainer.css('background-color', '');
|
||||
elem.css('background-color', '');
|
||||
}
|
||||
|
||||
drilldownTooltip.detach();
|
||||
});
|
||||
elem.html(body);
|
||||
|
||||
elem.mousemove(function(e) {
|
||||
if (!linkInfo) { return;}
|
||||
if (panel.sparkline.show) {
|
||||
addSparkline();
|
||||
}
|
||||
|
||||
drilldownTooltip.text('click to go to: ' + linkInfo.title);
|
||||
drilldownTooltip.place_tt(e.pageX+20, e.pageY-15);
|
||||
});
|
||||
elem.toggleClass('pointer', panel.links.length > 0);
|
||||
|
||||
if (panel.links.length > 0) {
|
||||
linkInfo = linkSrv.getPanelLinkAnchorInfo(panel.links[0], panel.scopedVars);
|
||||
} else {
|
||||
linkInfo = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function hookupDrilldownLinkTooltip() {
|
||||
// drilldown link tooltip
|
||||
var drilldownTooltip = $('<div id="tooltip" class="">hello</div>"');
|
||||
|
||||
elem.mouseleave(function() {
|
||||
if (panel.links.length === 0) { return;}
|
||||
drilldownTooltip.detach();
|
||||
});
|
||||
|
||||
elem.click(function(evt) {
|
||||
if (!linkInfo) { return; }
|
||||
// ignore title clicks in title
|
||||
if ($(evt).parents('.panel-header').length > 0) { return; }
|
||||
|
||||
if (linkInfo.target === '_blank') {
|
||||
var redirectWindow = window.open(linkInfo.href, '_blank');
|
||||
redirectWindow.location;
|
||||
return;
|
||||
}
|
||||
|
||||
if (linkInfo.href.indexOf('http') === 0) {
|
||||
window.location.href = linkInfo.href;
|
||||
} else {
|
||||
$timeout(function() {
|
||||
$location.url(linkInfo.href);
|
||||
});
|
||||
}
|
||||
|
||||
drilldownTooltip.detach();
|
||||
});
|
||||
|
||||
elem.mousemove(function(e) {
|
||||
if (!linkInfo) { return;}
|
||||
|
||||
drilldownTooltip.text('click to go to: ' + linkInfo.title);
|
||||
drilldownTooltip.place_tt(e.pageX+20, e.pageY-15);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getColorForValue(data, value) {
|
||||
@@ -226,8 +229,11 @@ function getColorForValue(data, value) {
|
||||
return data.colorMap[i];
|
||||
}
|
||||
}
|
||||
|
||||
return _.first(data.colorMap);
|
||||
}
|
||||
|
||||
export {singleStatPanel as panel, getColorForValue};
|
||||
export {
|
||||
SingleStatPanel,
|
||||
SingleStatPanel as Panel,
|
||||
getColorForValue
|
||||
};
|
||||
|
||||
@@ -9,9 +9,6 @@ import angular from 'angular';
|
||||
import helpers from '../../../../../test/specs/helpers';
|
||||
import {SingleStatCtrl} from '../controller';
|
||||
|
||||
|
||||
angular.module('grafana.controllers').controller('SingleStatCtrl', SingleStatCtrl);
|
||||
|
||||
describe('SingleStatCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
|
||||
@@ -25,7 +22,7 @@ describe('SingleStatCtrl', function() {
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
beforeEach(ctx.createControllerPhase('SingleStatCtrl'));
|
||||
beforeEach(ctx.createPanelController(SingleStatCtrl));
|
||||
|
||||
beforeEach(function() {
|
||||
setupFunc();
|
||||
@@ -33,9 +30,9 @@ describe('SingleStatCtrl', function() {
|
||||
data: [{target: 'test.cpu1', datapoints: ctx.datapoints}]
|
||||
}));
|
||||
|
||||
ctx.scope.refreshData(ctx.datasource);
|
||||
ctx.ctrl.refreshData(ctx.datasource);
|
||||
ctx.scope.$digest();
|
||||
ctx.data = ctx.scope.data;
|
||||
ctx.data = ctx.ctrl.data;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -76,7 +73,7 @@ describe('SingleStatCtrl', function() {
|
||||
singleStatScenario('When value to text mapping is specified', function(ctx) {
|
||||
ctx.setup(function() {
|
||||
ctx.datapoints = [[10,1]];
|
||||
ctx.scope.panel.valueMaps = [{value: '10', text: 'OK'}];
|
||||
ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
|
||||
});
|
||||
|
||||
it('Should replace value with text', function() {
|
||||
|
||||
@@ -4,132 +4,130 @@ import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import * as FileExport from 'app/core/utils/file_export';
|
||||
import PanelMeta from 'app/features/panel/panel_meta2';
|
||||
import {MetricsPanelCtrl} from '../../../features/panel/panel';
|
||||
import {transformDataToTable} from './transformers';
|
||||
import {tablePanelEditor} from './editor';
|
||||
|
||||
export class TablePanelCtrl {
|
||||
var panelDefaults = {
|
||||
targets: [{}],
|
||||
transform: 'timeseries_to_columns',
|
||||
pageSize: null,
|
||||
showHeader: true,
|
||||
styles: [
|
||||
{
|
||||
type: 'date',
|
||||
pattern: 'Time',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
thresholds: [],
|
||||
}
|
||||
],
|
||||
columns: [],
|
||||
scroll: true,
|
||||
fontSize: '100%',
|
||||
sort: {col: 0, desc: true},
|
||||
};
|
||||
|
||||
export class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
pageIndex: number;
|
||||
dataRaw: any;
|
||||
table: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $rootScope, $q, panelSrv, panelHelper, annotationsSrv) {
|
||||
$scope.ctrl = this;
|
||||
$scope.pageIndex = 0;
|
||||
constructor($scope, $injector, private annotationsSrv) {
|
||||
super($scope, $injector);
|
||||
this.pageIndex = 0;
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Table',
|
||||
editIcon: "fa fa-table",
|
||||
fullscreen: true,
|
||||
metricsEditor: true,
|
||||
});
|
||||
if (this.panel.styles === void 0) {
|
||||
this.panel.styles = this.panel.columns;
|
||||
this.panel.columns = this.panel.fields;
|
||||
delete this.panel.columns;
|
||||
delete this.panel.fields;
|
||||
}
|
||||
|
||||
$scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/table/options.html');
|
||||
$scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||
$scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
}
|
||||
|
||||
var panelDefaults = {
|
||||
targets: [{}],
|
||||
transform: 'timeseries_to_columns',
|
||||
pageSize: null,
|
||||
showHeader: true,
|
||||
styles: [
|
||||
{
|
||||
type: 'date',
|
||||
pattern: 'Time',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
thresholds: [],
|
||||
}
|
||||
],
|
||||
columns: [],
|
||||
scroll: true,
|
||||
fontSize: '100%',
|
||||
sort: {col: 0, desc: true},
|
||||
};
|
||||
initEditMode() {
|
||||
super.initEditMode();
|
||||
this.addEditorTab('Options', tablePanelEditor, 1);
|
||||
}
|
||||
|
||||
$scope.init = function() {
|
||||
if ($scope.panel.styles === void 0) {
|
||||
$scope.panel.styles = $scope.panel.columns;
|
||||
$scope.panel.columns = $scope.panel.fields;
|
||||
delete $scope.panel.columns;
|
||||
delete $scope.panel.fields;
|
||||
}
|
||||
getExtendedMenu() {
|
||||
var menu = super.getExtendedMenu();
|
||||
menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'});
|
||||
return menu;
|
||||
}
|
||||
|
||||
_.defaults($scope.panel, panelDefaults);
|
||||
panelSrv.init($scope);
|
||||
};
|
||||
refreshData(datasource) {
|
||||
this.pageIndex = 0;
|
||||
|
||||
$scope.refreshData = function(datasource) {
|
||||
panelHelper.updateTimeRange($scope);
|
||||
|
||||
$scope.pageIndex = 0;
|
||||
|
||||
if ($scope.panel.transform === 'annotations') {
|
||||
return annotationsSrv.getAnnotations($scope.dashboard).then(annotations => {
|
||||
$scope.dataRaw = annotations;
|
||||
$scope.render();
|
||||
});
|
||||
}
|
||||
|
||||
return panelHelper.issueMetricQuery($scope, datasource)
|
||||
.then($scope.dataHandler, function(err) {
|
||||
$scope.render();
|
||||
throw err;
|
||||
if (this.panel.transform === 'annotations') {
|
||||
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
|
||||
this.dataRaw = annotations;
|
||||
this.render();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.toggleColumnSort = function(col, colIndex) {
|
||||
if ($scope.panel.sort.col === colIndex) {
|
||||
if ($scope.panel.sort.desc) {
|
||||
$scope.panel.sort.desc = false;
|
||||
} else {
|
||||
$scope.panel.sort.col = null;
|
||||
}
|
||||
return this.issueQueries(datasource)
|
||||
.then(this.dataHandler.bind(this))
|
||||
.catch(err => {
|
||||
this.render();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
toggleColumnSort(col, colIndex) {
|
||||
if (this.panel.sort.col === colIndex) {
|
||||
if (this.panel.sort.desc) {
|
||||
this.panel.sort.desc = false;
|
||||
} else {
|
||||
$scope.panel.sort.col = colIndex;
|
||||
$scope.panel.sort.desc = true;
|
||||
this.panel.sort.col = null;
|
||||
}
|
||||
} else {
|
||||
this.panel.sort.col = colIndex;
|
||||
this.panel.sort.desc = true;
|
||||
}
|
||||
|
||||
$scope.render();
|
||||
};
|
||||
this.render();
|
||||
}
|
||||
|
||||
$scope.dataHandler = function(results) {
|
||||
$scope.dataRaw = results.data;
|
||||
$scope.pageIndex = 0;
|
||||
$scope.render();
|
||||
};
|
||||
dataHandler(results) {
|
||||
this.dataRaw = results.data;
|
||||
this.pageIndex = 0;
|
||||
this.render();
|
||||
}
|
||||
|
||||
$scope.render = function() {
|
||||
// automatically correct transform mode
|
||||
// based on data
|
||||
if ($scope.dataRaw && $scope.dataRaw.length) {
|
||||
if ($scope.dataRaw[0].type === 'table') {
|
||||
$scope.panel.transform = 'table';
|
||||
render() {
|
||||
// automatically correct transform mode
|
||||
// based on data
|
||||
if (this.dataRaw && this.dataRaw.length) {
|
||||
if (this.dataRaw[0].type === 'table') {
|
||||
this.panel.transform = 'table';
|
||||
} else {
|
||||
if (this.dataRaw[0].type === 'docs') {
|
||||
this.panel.transform = 'json';
|
||||
} else {
|
||||
if ($scope.dataRaw[0].type === 'docs') {
|
||||
$scope.panel.transform = 'json';
|
||||
} else {
|
||||
if ($scope.panel.transform === 'table' || $scope.panel.transform === 'json') {
|
||||
$scope.panel.transform = 'timeseries_to_rows';
|
||||
}
|
||||
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
|
||||
this.panel.transform = 'timeseries_to_rows';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.table = transformDataToTable($scope.dataRaw, $scope.panel);
|
||||
$scope.table.sort($scope.panel.sort);
|
||||
panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw);
|
||||
};
|
||||
this.table = transformDataToTable(this.dataRaw, this.panel);
|
||||
this.table.sort(this.panel.sort);
|
||||
this.broadcastRender(this.table);
|
||||
}
|
||||
|
||||
$scope.exportCsv = function() {
|
||||
FileExport.exportTableDataToCsv($scope.table);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
exportCsv() {
|
||||
FileExport.exportTableDataToCsv(this.table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-large tight-form-input"
|
||||
ng-model="panel.transform"
|
||||
ng-options="k as v.description for (k, v) in transformers"
|
||||
ng-change="transformChanged()"></select>
|
||||
ng-model="editor.panel.transform"
|
||||
ng-options="k as v.description for (k, v) in editor.transformers"
|
||||
ng-change="editor.transformChanged()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -21,14 +21,14 @@
|
||||
<li class="tight-form-item" style="width: 140px">
|
||||
Columns
|
||||
</li>
|
||||
<li class="tight-form-item" ng-repeat="column in panel.columns">
|
||||
<i class="pointer fa fa-remove" ng-click="removeColumn(column)"></i>
|
||||
<li class="tight-form-item" ng-repeat="column in editor.panel.columns">
|
||||
<i class="pointer fa fa-remove" ng-click="editor.removeColumn(column)"></i>
|
||||
<span>
|
||||
{{column.text}}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="addColumnSegment" get-options="getColumnOptions()" on-change="addColumn()"></metric-segment>
|
||||
<metric-segment segment="editor.addColumnSegment" get-options="editor.getColumnOptions()" on-change="editor.addColumn()"></metric-segment>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -46,16 +46,16 @@
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" placeholder="100"
|
||||
empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
|
||||
empty-to-null ng-model="editor.panel.pageSize" ng-change="editor.render()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Scroll" model="panel.scroll" change="render()"></editor-checkbox>
|
||||
<editor-checkbox text="Scroll" model="editor.panel.scroll" change="editor.render()"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Font size
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="panel.fontSize" ng-options="f for f in fontSizes" ng-change="render()"></select>
|
||||
<select class="input-small tight-form-input" ng-model="editor.panel.fontSize" ng-options="f for f in editor.fontSizes" ng-change="editor.render()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -68,11 +68,11 @@
|
||||
<h5>Column Styles</h5>
|
||||
|
||||
<div class="tight-form-container">
|
||||
<div ng-repeat="style in panel.styles">
|
||||
<div ng-repeat="style in editor.panel.styles">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list pull-right">
|
||||
<li class="tight-form-item last">
|
||||
<i class="fa fa-remove pointer" ng-click="removeColumnStyle(style)"></i>
|
||||
<i class="fa fa-remove pointer" ng-click="editor.removeColumnStyle(style)"></i>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
Name or regex
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" ng-model="style.pattern" bs-typeahead="getColumnNames" ng-blur="render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
|
||||
<input type="text" ng-model="style.pattern" bs-typeahead="editor.getColumnNames" ng-blur="editor.render()" data-min-length=0 data-items=100 class="input-medium tight-form-input">
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 86px">
|
||||
Type
|
||||
@@ -89,8 +89,8 @@
|
||||
<li>
|
||||
<select class="input-small tight-form-input"
|
||||
ng-model="style.type"
|
||||
ng-options="c.value as c.text for c in columnTypes"
|
||||
ng-change="render()"
|
||||
ng-options="c.value as c.text for c in editor.columnTypes"
|
||||
ng-change="editor.render()"
|
||||
style="width: 150px"
|
||||
></select>
|
||||
</li>
|
||||
@@ -100,7 +100,7 @@
|
||||
Format
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="style.dateFormat" options="dateFormats" on-change="render()" custom="true"></metric-segment-model>
|
||||
<metric-segment-model property="style.dateFormat" options="editor.dateFormats" on-change="editor.render()" custom="true"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -113,8 +113,8 @@
|
||||
<li>
|
||||
<select class="input-small tight-form-input"
|
||||
ng-model="style.colorMode"
|
||||
ng-options="c.value as c.text for c in colorModes"
|
||||
ng-change="render()"
|
||||
ng-options="c.value as c.text for c in editor.colorModes"
|
||||
ng-change="editor.render()"
|
||||
style="width: 150px"
|
||||
></select>
|
||||
</li>
|
||||
@@ -122,18 +122,18 @@
|
||||
Thresholds<tip>Comma seperated values</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="style.thresholds" ng-blur="render()" placeholder="0,50,80" array-join></input>
|
||||
<input type="text" class="input-small tight-form-input" style="width: 150px" ng-model="style.thresholds" ng-blur="editor.render()" placeholder="0,50,80" array-join></input>
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 60px">
|
||||
Colors
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<spectrum-picker ng-model="style.colors[0]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="style.colors[1]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="style.colors[2]" ng-change="render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="style.colors[0]" ng-change="editor.render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="style.colors[1]" ng-change="editor.render()" ></spectrum-picker>
|
||||
<spectrum-picker ng-model="style.colors[2]" ng-change="editor.render()" ></spectrum-picker>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<a class="pointer" ng-click="invertColorOrder($index)">invert order</a>
|
||||
<a class="pointer" ng-click="editor.invertColorOrder($index)">invert order</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
@@ -145,8 +145,8 @@
|
||||
</li>
|
||||
<li class="dropdown" style="width: 150px"
|
||||
ng-model="style.unit"
|
||||
dropdown-typeahead="unitFormats"
|
||||
dropdown-typeahead-on-select="setUnitFormat(style, $subItem)">
|
||||
dropdown-typeahead="editor.unitFormats"
|
||||
dropdown-typeahead-on-select="editor.setUnitFormat(style, $subItem)">
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 86px">
|
||||
Decimals
|
||||
@@ -161,7 +161,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="addColumnStyle()">
|
||||
<button class="btn btn-inverse" style="margin-top: 20px" ng-click="editor.addColumnStyle()">
|
||||
Add column style rule
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -10,105 +10,123 @@ import {transformers} from './transformers';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
|
||||
export class TablePanelEditorCtrl {
|
||||
panel: any;
|
||||
panelCtrl: any;
|
||||
transformers: any;
|
||||
colorModes: any;
|
||||
columnStyles: any;
|
||||
columnTypes: any;
|
||||
fontSizes: any;
|
||||
dateFormats: any;
|
||||
addColumnSegment: any;
|
||||
unitFormats: any;
|
||||
getColumnNames: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $q, uiSegmentSrv) {
|
||||
$scope.transformers = transformers;
|
||||
$scope.unitFormats = kbn.getUnitFormats();
|
||||
$scope.colorModes = [
|
||||
constructor($scope, private $q, private uiSegmentSrv) {
|
||||
$scope.editor = this;
|
||||
this.panelCtrl = $scope.ctrl;
|
||||
this.panel = this.panelCtrl.panel;
|
||||
this.transformers = transformers;
|
||||
this.unitFormats = kbn.getUnitFormats();
|
||||
this.colorModes = [
|
||||
{text: 'Disabled', value: null},
|
||||
{text: 'Cell', value: 'cell'},
|
||||
{text: 'Value', value: 'value'},
|
||||
{text: 'Row', value: 'row'},
|
||||
];
|
||||
$scope.columnTypes = [
|
||||
this.columnTypes = [
|
||||
{text: 'Number', value: 'number'},
|
||||
{text: 'String', value: 'string'},
|
||||
{text: 'Date', value: 'date'},
|
||||
];
|
||||
$scope.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
|
||||
$scope.dateFormats = [
|
||||
this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
|
||||
this.dateFormats = [
|
||||
{text: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss'},
|
||||
{text: 'MM/DD/YY h:mm:ss a', value: 'MM/DD/YY h:mm:ss a'},
|
||||
{text: 'MMMM D, YYYY LT', value: 'MMMM D, YYYY LT'},
|
||||
];
|
||||
|
||||
$scope.addColumnSegment = uiSegmentSrv.newPlusButton();
|
||||
this.addColumnSegment = uiSegmentSrv.newPlusButton();
|
||||
|
||||
$scope.getColumnOptions = function() {
|
||||
if (!$scope.dataRaw) {
|
||||
return $q.when([]);
|
||||
}
|
||||
var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw);
|
||||
var segments = _.map(columns, (c: any) => uiSegmentSrv.newSegment({value: c.text}));
|
||||
return $q.when(segments);
|
||||
};
|
||||
|
||||
$scope.addColumn = function() {
|
||||
var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw);
|
||||
var column = _.findWhere(columns, {text: $scope.addColumnSegment.value});
|
||||
|
||||
if (column) {
|
||||
$scope.panel.columns.push(column);
|
||||
$scope.render();
|
||||
}
|
||||
|
||||
var plusButton = uiSegmentSrv.newPlusButton();
|
||||
$scope.addColumnSegment.html = plusButton.html;
|
||||
$scope.addColumnSegment.value = plusButton.value;
|
||||
};
|
||||
|
||||
$scope.transformChanged = function() {
|
||||
$scope.panel.columns = [];
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.removeColumn = function(column) {
|
||||
$scope.panel.columns = _.without($scope.panel.columns, column);
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.setUnitFormat = function(column, subItem) {
|
||||
column.unit = subItem.value;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.addColumnStyle = function() {
|
||||
var columnStyleDefaults = {
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
thresholds: [],
|
||||
};
|
||||
|
||||
$scope.panel.styles.push(angular.copy(columnStyleDefaults));
|
||||
};
|
||||
|
||||
$scope.removeColumnStyle = function(style) {
|
||||
$scope.panel.styles = _.without($scope.panel.styles, style);
|
||||
};
|
||||
|
||||
$scope.getColumnNames = function() {
|
||||
if (!$scope.table) {
|
||||
// this is used from bs-typeahead and needs to be instance bound
|
||||
this.getColumnNames = () => {
|
||||
if (!this.panelCtrl.table) {
|
||||
return [];
|
||||
}
|
||||
return _.map($scope.table.columns, function(col: any) {
|
||||
return _.map(this.panelCtrl.table.columns, function(col: any) {
|
||||
return col.text;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.invertColorOrder = function(index) {
|
||||
var ref = $scope.panel.styles[index].colors;
|
||||
var copy = ref[0];
|
||||
ref[0] = ref[2];
|
||||
ref[2] = copy;
|
||||
$scope.render();
|
||||
getColumnOptions() {
|
||||
if (!this.panelCtrl.dataRaw) {
|
||||
return this.$q.when([]);
|
||||
}
|
||||
var columns = this.transformers[this.panel.transform].getColumns(this.panelCtrl.dataRaw);
|
||||
var segments = _.map(columns, (c: any) => this.uiSegmentSrv.newSegment({value: c.text}));
|
||||
return this.$q.when(segments);
|
||||
}
|
||||
|
||||
addColumn() {
|
||||
var columns = transformers[this.panel.transform].getColumns(this.panelCtrl.dataRaw);
|
||||
var column = _.findWhere(columns, {text: this.addColumnSegment.value});
|
||||
|
||||
if (column) {
|
||||
this.panel.columns.push(column);
|
||||
this.render();
|
||||
}
|
||||
|
||||
var plusButton = this.uiSegmentSrv.newPlusButton();
|
||||
this.addColumnSegment.html = plusButton.html;
|
||||
this.addColumnSegment.value = plusButton.value;
|
||||
}
|
||||
|
||||
transformChanged() {
|
||||
this.panel.columns = [];
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.panelCtrl.render();
|
||||
}
|
||||
|
||||
removeColumn(column) {
|
||||
this.panel.columns = _.without(this.panel.columns, column);
|
||||
this.panelCtrl.render();
|
||||
}
|
||||
|
||||
setUnitFormat(column, subItem) {
|
||||
column.unit = subItem.value;
|
||||
this.panelCtrl.render();
|
||||
};
|
||||
|
||||
addColumnStyle() {
|
||||
var columnStyleDefaults = {
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
thresholds: [],
|
||||
};
|
||||
|
||||
this.panel.styles.push(angular.copy(columnStyleDefaults));
|
||||
}
|
||||
|
||||
removeColumnStyle(style) {
|
||||
this.panel.styles = _.without(this.panel.styles, style);
|
||||
}
|
||||
|
||||
invertColorOrder(index) {
|
||||
var ref = this.panel.styles[index].colors;
|
||||
var copy = ref[0];
|
||||
ref[0] = ref[2];
|
||||
ref[2] = copy;
|
||||
this.panelCtrl.render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
<div class="table-panel-wrapper">
|
||||
<grafana-panel>
|
||||
<div class="table-panel-container">
|
||||
<div class="table-panel-header-bg"></div>
|
||||
<div class="table-panel-scroll">
|
||||
<table class="table-panel-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="col in table.columns">
|
||||
<div class="table-panel-table-header-inner pointer" ng-click="toggleColumnSort(col, $index)">
|
||||
{{col.text}}
|
||||
<span class="table-panel-table-header-controls" ng-if="col.sort">
|
||||
<i class="fa fa-caret-down" ng-show="col.desc"></i>
|
||||
<i class="fa fa-caret-up" ng-hide="col.desc"></i>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-panel-footer">
|
||||
</div>
|
||||
</grafana-panel>
|
||||
<div class="table-panel-container">
|
||||
<div class="table-panel-header-bg"></div>
|
||||
<div class="table-panel-scroll">
|
||||
<table class="table-panel-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="col in ctrl.table.columns">
|
||||
<div class="table-panel-table-header-inner pointer" ng-click="ctrl.toggleColumnSort(col, $index)">
|
||||
{{col.text}}
|
||||
<span class="table-panel-table-header-controls" ng-if="col.sort">
|
||||
<i class="fa fa-caret-down" ng-show="col.desc"></i>
|
||||
<i class="fa fa-caret-up" ng-hide="col.desc"></i>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-panel-footer">
|
||||
</div>
|
||||
|
||||
@@ -5,103 +5,98 @@ import kbn = require('app/core/utils/kbn');
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import angular from 'angular';
|
||||
import {PanelDirective} from '../../../features/panel/panel';
|
||||
import {TablePanelCtrl} from './controller';
|
||||
import {TableRenderer} from './renderer';
|
||||
import {tablePanelEditor} from './editor';
|
||||
|
||||
angular.module('grafana.directives').directive('grafanaPanelTableEditor', tablePanelEditor);
|
||||
class TablePanel extends PanelDirective {
|
||||
templateUrl = 'app/plugins/panel/table/module.html';
|
||||
controller = TablePanelCtrl;
|
||||
|
||||
function tablePanel() {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'app/plugins/panel/table/module.html',
|
||||
controller: TablePanelCtrl,
|
||||
link: function(scope, elem) {
|
||||
var data;
|
||||
var panel = scope.panel;
|
||||
var pageCount = 0;
|
||||
var formaters = [];
|
||||
link(scope, elem, attrs, ctrl) {
|
||||
var data;
|
||||
var panel = ctrl.panel;
|
||||
var pageCount = 0;
|
||||
var formaters = [];
|
||||
|
||||
function getTableHeight() {
|
||||
var panelHeight = scope.height || scope.panel.height || scope.row.height;
|
||||
if (_.isString(panelHeight)) {
|
||||
panelHeight = parseInt(panelHeight.replace('px', ''), 10);
|
||||
}
|
||||
if (pageCount > 1) {
|
||||
panelHeight -= 28;
|
||||
}
|
||||
|
||||
return (panelHeight - 60) + 'px';
|
||||
function getTableHeight() {
|
||||
var panelHeight = ctrl.height || ctrl.panel.height || ctrl.row.height;
|
||||
if (_.isString(panelHeight)) {
|
||||
panelHeight = parseInt(panelHeight.replace('px', ''), 10);
|
||||
}
|
||||
if (pageCount > 1) {
|
||||
panelHeight -= 28;
|
||||
}
|
||||
|
||||
function appendTableRows(tbodyElem) {
|
||||
var renderer = new TableRenderer(panel, data, scope.dashboard.timezone);
|
||||
tbodyElem.empty();
|
||||
tbodyElem.html(renderer.render(scope.pageIndex));
|
||||
}
|
||||
|
||||
function switchPage(e) {
|
||||
var el = $(e.currentTarget);
|
||||
scope.pageIndex = (parseInt(el.text(), 10)-1);
|
||||
renderPanel();
|
||||
}
|
||||
|
||||
function appendPaginationControls(footerElem) {
|
||||
footerElem.empty();
|
||||
|
||||
var pageSize = panel.pageSize || 100;
|
||||
pageCount = Math.ceil(data.rows.length / pageSize);
|
||||
if (pageCount === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var startPage = Math.max(scope.pageIndex - 3, 0);
|
||||
var endPage = Math.min(pageCount, startPage + 9);
|
||||
|
||||
var paginationList = $('<ul></ul>');
|
||||
|
||||
for (var i = startPage; i < endPage; i++) {
|
||||
var activeClass = i === scope.pageIndex ? 'active' : '';
|
||||
var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
|
||||
paginationList.append(pageLinkElem);
|
||||
}
|
||||
|
||||
footerElem.append(paginationList);
|
||||
}
|
||||
|
||||
function renderPanel() {
|
||||
var container = elem.find('.table-panel-container');
|
||||
var rootElem = elem.find('.table-panel-scroll');
|
||||
var tbodyElem = elem.find('tbody');
|
||||
var footerElem = elem.find('.table-panel-footer');
|
||||
|
||||
appendTableRows(tbodyElem);
|
||||
|
||||
container.css({'font-size': panel.fontSize});
|
||||
appendPaginationControls(footerElem);
|
||||
|
||||
rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });
|
||||
}
|
||||
|
||||
elem.on('click', '.table-panel-page-link', switchPage);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
elem.off('click', '.table-panel-page-link');
|
||||
});
|
||||
|
||||
scope.$on('render', function(event, renderData) {
|
||||
data = renderData || data;
|
||||
if (!data) {
|
||||
scope.get_data();
|
||||
return;
|
||||
}
|
||||
|
||||
renderPanel();
|
||||
});
|
||||
return (panelHeight - 60) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
function appendTableRows(tbodyElem) {
|
||||
var renderer = new TableRenderer(panel, data, ctrl.dashboard.timezone);
|
||||
tbodyElem.empty();
|
||||
tbodyElem.html(renderer.render(ctrl.pageIndex));
|
||||
}
|
||||
|
||||
function switchPage(e) {
|
||||
var el = $(e.currentTarget);
|
||||
ctrl.pageIndex = (parseInt(el.text(), 10)-1);
|
||||
renderPanel();
|
||||
}
|
||||
|
||||
function appendPaginationControls(footerElem) {
|
||||
footerElem.empty();
|
||||
|
||||
var pageSize = panel.pageSize || 100;
|
||||
pageCount = Math.ceil(data.rows.length / pageSize);
|
||||
if (pageCount === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var startPage = Math.max(ctrl.pageIndex - 3, 0);
|
||||
var endPage = Math.min(pageCount, startPage + 9);
|
||||
|
||||
var paginationList = $('<ul></ul>');
|
||||
|
||||
for (var i = startPage; i < endPage; i++) {
|
||||
var activeClass = i === ctrl.pageIndex ? 'active' : '';
|
||||
var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
|
||||
paginationList.append(pageLinkElem);
|
||||
}
|
||||
|
||||
footerElem.append(paginationList);
|
||||
}
|
||||
|
||||
function renderPanel() {
|
||||
var panelElem = elem.parents('.panel');
|
||||
var rootElem = elem.find('.table-panel-scroll');
|
||||
var tbodyElem = elem.find('tbody');
|
||||
var footerElem = elem.find('.table-panel-footer');
|
||||
|
||||
elem.css({'font-size': panel.fontSize});
|
||||
panelElem.addClass('table-panel-wrapper');
|
||||
|
||||
appendTableRows(tbodyElem);
|
||||
appendPaginationControls(footerElem);
|
||||
|
||||
rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });
|
||||
}
|
||||
|
||||
elem.on('click', '.table-panel-page-link', switchPage);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
elem.off('click', '.table-panel-page-link');
|
||||
});
|
||||
|
||||
scope.$on('render', function(event, renderData) {
|
||||
data = renderData || data;
|
||||
if (data) {
|
||||
renderPanel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {tablePanel as panel};
|
||||
export {
|
||||
TablePanel,
|
||||
TablePanel as Panel
|
||||
};
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<div>
|
||||
<div class="row-fluid">
|
||||
<div class="span4">
|
||||
<label class="small">Mode</label> <select class="input-medium" ng-model="panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
|
||||
<label class="small">Mode</label> <select class="input-medium" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
|
||||
</div>
|
||||
<div class="span2" ng-show="panel.mode == 'text'">
|
||||
<label class="small">Font Size</label> <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select>
|
||||
<div class="span2" ng-show="ctrl.panel.mode == 'text'">
|
||||
<label class="small">Font Size</label> <select class="input-mini" ng-model="ctrl.panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class=small>Content
|
||||
<span ng-show="panel.mode == 'markdown'">(This area uses <a target="_blank" href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>. HTML is not supported)</span>
|
||||
<span ng-show="ctrl.panel.mode == 'markdown'">(This area uses <a target="_blank" href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>. HTML is not supported)</span>
|
||||
</label>
|
||||
|
||||
<textarea ng-model="panel.content" rows="20" style="width:95%" ng-change="render()" ng-model-onblur>
|
||||
<textarea ng-model="ctrl.panel.content" rows="20" style="width:95%" ng-change="ctrl.render()" ng-model-onblur>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
<grafana-panel>
|
||||
<p ng-bind-html="content" ng-show="content"></p>
|
||||
</grafana-panel>
|
||||
<p ng-bind-html="ctrl.content" ng-show="ctrl.content"></p>
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'app/app',
|
||||
'lodash',
|
||||
'require',
|
||||
'app/features/panel/panel_meta',
|
||||
],
|
||||
function (angular, app, _, require, PanelMeta) {
|
||||
'use strict';
|
||||
|
||||
var converter;
|
||||
|
||||
/** @ngInject */
|
||||
function TextPanelCtrl($scope, templateSrv, $sce, panelSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Text',
|
||||
editIcon: "fa fa-text-width",
|
||||
fullscreen: true,
|
||||
});
|
||||
|
||||
$scope.panelMeta.addEditorTab('Edit text', 'app/plugins/panel/text/editor.html');
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
title : 'default title',
|
||||
mode : "markdown", // 'html', 'markdown', 'text'
|
||||
content : "",
|
||||
style: {},
|
||||
};
|
||||
|
||||
_.defaults($scope.panel, _d);
|
||||
|
||||
$scope.init = function() {
|
||||
panelSrv.init($scope);
|
||||
$scope.ready = false;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.refreshData = function() {
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.render();
|
||||
};
|
||||
|
||||
$scope.render = function() {
|
||||
if ($scope.panel.mode === 'markdown') {
|
||||
$scope.renderMarkdown($scope.panel.content);
|
||||
}
|
||||
else if ($scope.panel.mode === 'html') {
|
||||
$scope.updateContent($scope.panel.content);
|
||||
}
|
||||
else if ($scope.panel.mode === 'text') {
|
||||
$scope.renderText($scope.panel.content);
|
||||
}
|
||||
$scope.panelRenderingComplete();
|
||||
};
|
||||
|
||||
$scope.renderText = function(content) {
|
||||
content = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/\n/g, '<br/>');
|
||||
|
||||
$scope.updateContent(content);
|
||||
};
|
||||
|
||||
$scope.renderMarkdown = function(content) {
|
||||
var text = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<');
|
||||
|
||||
if (converter) {
|
||||
$scope.updateContent(converter.makeHtml(text));
|
||||
}
|
||||
else {
|
||||
require(['vendor/showdown'], function (Showdown) {
|
||||
converter = new Showdown.converter();
|
||||
$scope.updateContent(converter.makeHtml(text));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.updateContent = function(html) {
|
||||
try {
|
||||
$scope.content = $sce.trustAsHtml(templateSrv.replace(html, $scope.panel.scopedVars));
|
||||
} catch(e) {
|
||||
console.log('Text panel error: ', e);
|
||||
$scope.content = $sce.trustAsHtml(html);
|
||||
}
|
||||
if(!$scope.$$phase) {
|
||||
$scope.$digest();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openEditor = function() {
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
}
|
||||
|
||||
function textPanel() {
|
||||
return {
|
||||
controller: TextPanelCtrl,
|
||||
templateUrl: 'app/plugins/panel/text/module.html',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
panel: textPanel,
|
||||
};
|
||||
});
|
||||
85
public/app/plugins/panel/text/module.ts
Normal file
85
public/app/plugins/panel/text/module.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import {PanelDirective, PanelCtrl} from '../../../features/panel/panel';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
mode : "markdown", // 'html', 'markdown', 'text'
|
||||
content : "# title",
|
||||
};
|
||||
|
||||
export class TextPanelCtrl extends PanelCtrl {
|
||||
converter: any;
|
||||
content: string;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private templateSrv, private $sce) {
|
||||
super($scope, $injector);
|
||||
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
this.icon = 'fa fa-text-width';
|
||||
this.addEditorTab('Options', 'public/app/plugins/panel/text/editor.html');
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.panel.mode === 'markdown') {
|
||||
this.renderMarkdown(this.panel.content);
|
||||
} else if (this.panel.mode === 'html') {
|
||||
this.updateContent(this.panel.content);
|
||||
} else if (this.panel.mode === 'text') {
|
||||
this.renderText(this.panel.content);
|
||||
}
|
||||
this.renderingCompleted();
|
||||
}
|
||||
|
||||
renderText(content) {
|
||||
content = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/\n/g, '<br/>');
|
||||
this.updateContent(content);
|
||||
}
|
||||
|
||||
renderMarkdown(content) {
|
||||
var text = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<');
|
||||
|
||||
if (this.converter) {
|
||||
this.updateContent(this.converter.makeHtml(text));
|
||||
} else {
|
||||
System.import('vendor/showdown').then(Showdown => {
|
||||
this.converter = new Showdown.converter();
|
||||
this.$scope.$apply(() => {
|
||||
this.updateContent(this.converter.makeHtml(text));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateContent(html) {
|
||||
try {
|
||||
this.content = this.$sce.trustAsHtml(this.templateSrv.replace(html, this.panel.scopedVars));
|
||||
} catch (e) {
|
||||
console.log('Text panel error: ', e);
|
||||
this.content = this.$sce.trustAsHtml(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TextPanel extends PanelDirective {
|
||||
templateUrl = `app/plugins/panel/text/module.html`;
|
||||
controller = TextPanelCtrl;
|
||||
}
|
||||
|
||||
export {TextPanel as Panel}
|
||||
5
public/app/plugins/panel/unknown/module.html
Normal file
5
public/app/plugins/panel/unknown/module.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="text-center" style="padding-top: 2rem">
|
||||
Unknown panel type: <strong>{{ctrl.panel.type}}</strong>
|
||||
</div
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
export function unknownPanelDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: `
|
||||
<grafana-panel>
|
||||
<div class="text-center" style="padding-top: 2rem">
|
||||
Unknown panel type: <strong>{{panel.type}}</strong>
|
||||
</div>
|
||||
</grafana-panel>
|
||||
`,
|
||||
};
|
||||
import {PanelDirective} from '../../../features/panel/panel';
|
||||
|
||||
class UnknownPanel extends PanelDirective {
|
||||
templateUrl = 'public/app/plugins/panel/unknown/module.html';
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
UnknownPanel,
|
||||
UnknownPanel as Panel
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ define([
|
||||
beforeEach(module('grafana.services'));
|
||||
|
||||
beforeEach(inject(function(dashboardViewStateSrv, $location, $rootScope) {
|
||||
$rootScope.onAppEvent = function(){};
|
||||
$rootScope.onAppEvent = function() {};
|
||||
$rootScope.dashboard = {meta: {}};
|
||||
viewState = dashboardViewStateSrv.create($rootScope);
|
||||
location = $location;
|
||||
}));
|
||||
@@ -19,7 +20,7 @@ define([
|
||||
var updateState = { fullscreen: true, edit: true, panelId: 1 };
|
||||
viewState.update(updateState);
|
||||
expect(location.search()).to.eql(updateState);
|
||||
expect(viewState.fullscreen).to.be(true);
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(true);
|
||||
expect(viewState.state.fullscreen).to.be(true);
|
||||
});
|
||||
});
|
||||
@@ -29,7 +30,7 @@ define([
|
||||
viewState.update({fullscreen: true, panelId: 1, edit: true});
|
||||
viewState.update({fullscreen: false});
|
||||
expect(location.search()).to.eql({});
|
||||
expect(viewState.fullscreen).to.be(false);
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(false);
|
||||
expect(viewState.state.fullscreen).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
define([
|
||||
'lodash',
|
||||
'app/core/config',
|
||||
'app/core/utils/datemath',
|
||||
], function(_, dateMath) {
|
||||
], function(_, config, dateMath) {
|
||||
'use strict';
|
||||
|
||||
function ControllerTestContext() {
|
||||
@@ -36,6 +37,28 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
this.createPanelController = function(Ctrl) {
|
||||
return inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
self.scope = $rootScope.$new();
|
||||
self.$location = $location;
|
||||
self.$browser = $browser;
|
||||
self.$q = $q;
|
||||
self.panel = {type: 'test'};
|
||||
self.dashboard = {meta: {}};
|
||||
|
||||
$rootScope.appEvent = sinon.spy();
|
||||
$rootScope.onAppEvent = sinon.spy();
|
||||
$rootScope.colors = [];
|
||||
|
||||
for (var i = 0; i < 50; i++) { $rootScope.colors.push('#' + i); }
|
||||
|
||||
config.panels['test'] = {info: {}};
|
||||
self.ctrl = $controller(Ctrl, {$scope: self.scope}, {
|
||||
panel: self.panel, dashboard: self.dashboard
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.createControllerPhase = function(controllerName) {
|
||||
return inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
self.scope = $rootScope.$new();
|
||||
@@ -44,7 +67,7 @@ define([
|
||||
self.scope.contextSrv = {};
|
||||
self.scope.panel = {};
|
||||
self.scope.row = { panels:[] };
|
||||
self.scope.dashboard = {};
|
||||
self.scope.dashboard = {meta: {}};
|
||||
self.scope.dashboardMeta = {};
|
||||
self.scope.dashboardViewState = new DashboardViewStateStub();
|
||||
self.scope.appEvent = sinon.spy();
|
||||
@@ -59,7 +82,6 @@ define([
|
||||
self.controller = $controller(controllerName, {
|
||||
$scope: self.scope
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -74,10 +96,10 @@ define([
|
||||
self.$routeParams = {};
|
||||
|
||||
this.providePhase = function(mocks) {
|
||||
return module(function($provide) {
|
||||
_.each(mocks, function(key) {
|
||||
$provide.value(key, self[key]);
|
||||
});
|
||||
return module(function($provide) {
|
||||
_.each(mocks, function(key) {
|
||||
$provide.value(key, self[key]);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -9,16 +9,25 @@ define([
|
||||
var popoverSrv = {};
|
||||
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(module('grafana.panels.graph'));
|
||||
beforeEach(module('grafana.controllers'));
|
||||
|
||||
beforeEach(ctx.providePhase({
|
||||
popoverSrv: popoverSrv
|
||||
}));
|
||||
|
||||
beforeEach(ctx.createControllerPhase('SeriesOverridesCtrl'));
|
||||
beforeEach(function() {
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
// ctx.createControllerPhase('SeriesOverridesCtrl'));
|
||||
// beforeEach(function() {
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.scope.ctrl = {
|
||||
render: sinon.spy(),
|
||||
seriesList: []
|
||||
};
|
||||
ctx.scope.render = function() {};
|
||||
});
|
||||
ctx.controller = $controller('SeriesOverridesCtrl', {
|
||||
$scope: ctx.scope
|
||||
});
|
||||
}));
|
||||
|
||||
describe('When setting an override', function() {
|
||||
beforeEach(function() {
|
||||
|
||||
5
public/vendor/npm/angular2/animate.d.ts
vendored
5
public/vendor/npm/angular2/animate.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
export { Animation } from './src/animate/animation';
|
||||
export { AnimationBuilder } from './src/animate/animation_builder';
|
||||
export { BrowserDetails } from './src/animate/browser_details';
|
||||
export { CssAnimationBuilder } from './src/animate/css_animation_builder';
|
||||
export { CssAnimationOptions } from './src/animate/css_animation_options';
|
||||
6
public/vendor/npm/angular2/bootstrap.d.ts
vendored
6
public/vendor/npm/angular2/bootstrap.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
*/
|
||||
export { bootstrap } from 'angular2/platform/browser';
|
||||
export { AngularEntrypoint } from 'angular2/src/core/angular_entrypoint';
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
*/
|
||||
export { bootstrapStatic } from 'angular2/platform/browser_static';
|
||||
export { AngularEntrypoint } from 'angular2/src/core/angular_entrypoint';
|
||||
File diff suppressed because one or more lines are too long
36742
public/vendor/npm/angular2/bundles/angular2-all.umd.dev.js
vendored
36742
public/vendor/npm/angular2/bundles/angular2-all.umd.dev.js
vendored
File diff suppressed because one or more lines are too long
36741
public/vendor/npm/angular2/bundles/angular2-all.umd.js
vendored
36741
public/vendor/npm/angular2/bundles/angular2-all.umd.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
3284
public/vendor/npm/angular2/bundles/angular2-polyfills.js
vendored
3284
public/vendor/npm/angular2/bundles/angular2-polyfills.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
25074
public/vendor/npm/angular2/bundles/angular2.dev.js
vendored
25074
public/vendor/npm/angular2/bundles/angular2.dev.js
vendored
File diff suppressed because it is too large
Load Diff
25074
public/vendor/npm/angular2/bundles/angular2.js
vendored
25074
public/vendor/npm/angular2/bundles/angular2.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1083
public/vendor/npm/angular2/bundles/http.dev.js
vendored
1083
public/vendor/npm/angular2/bundles/http.dev.js
vendored
File diff suppressed because it is too large
Load Diff
1083
public/vendor/npm/angular2/bundles/http.js
vendored
1083
public/vendor/npm/angular2/bundles/http.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2972
public/vendor/npm/angular2/bundles/router.dev.js
vendored
2972
public/vendor/npm/angular2/bundles/router.dev.js
vendored
File diff suppressed because it is too large
Load Diff
2972
public/vendor/npm/angular2/bundles/router.js
vendored
2972
public/vendor/npm/angular2/bundles/router.js
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user