mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
merge sync with master
This commit is contained in:
commit
3f945e886b
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,47 +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,11 +5,11 @@ 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;
|
||||
scope.ctrl.datasource = ds;
|
||||
|
||||
if (!scope.target.refId) {
|
||||
scope.target.refId = 'A';
|
||||
@ -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>
|
||||
|
||||
|
||||
|
@ -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,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('GraphiteQueryCtrl', function($scope, uiSegmentSrv, templateSrv) {
|
||||
var panelCtrl = $scope.panelCtrl = $scope.ctrl;
|
||||
|
||||
$scope.init = function() {
|
||||
if ($scope.target) {
|
||||
@ -125,7 +126,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
}
|
||||
|
||||
var path = getSegmentPathUpTo(fromIndex + 1);
|
||||
return $scope.datasource.metricFindQuery(path)
|
||||
return panelCtrl.datasource.metricFindQuery(path)
|
||||
.then(function(segments) {
|
||||
if (segments.length === 0) {
|
||||
if (path !== '') {
|
||||
@ -159,7 +160,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 panelCtrl.datasource.metricFindQuery(query).then(function(segments) {
|
||||
var altSegments = _.map(segments, function(segment) {
|
||||
return uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable });
|
||||
});
|
||||
@ -208,7 +209,7 @@ function (angular, _, config, gfunc, Parser) {
|
||||
|
||||
$scope.targetTextChanged = function() {
|
||||
parseTarget();
|
||||
$scope.get_data();
|
||||
panelCtrl.refresh();
|
||||
};
|
||||
|
||||
$scope.targetChanged = function() {
|
||||
@ -222,7 +223,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,19 @@ 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.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([]));
|
||||
ctx.panelCtrl.datasource = ctx.datasource;
|
||||
ctx.panelCtrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
@ -30,7 +36,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
});
|
||||
|
||||
it('should validate metric key exists', function() {
|
||||
expect(ctx.scope.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
|
||||
expect(ctx.panelCtrl.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
|
||||
});
|
||||
|
||||
it('should delete last segment if no metrics are found', function() {
|
||||
@ -45,11 +51,11 @@ describe('GraphiteQueryCtrl', function() {
|
||||
describe('when adding function', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = 'test.prod.*.count';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
|
||||
ctx.panelCtrl.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
|
||||
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,19 +67,17 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when adding function before any metric segment', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = '';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: true}]));
|
||||
ctx.panelCtrl.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'));
|
||||
});
|
||||
|
||||
@ -85,10 +89,9 @@ describe('GraphiteQueryCtrl', function() {
|
||||
describe('when initalizing target without metric expression and only function', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = 'asPercent(#A, #B)';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl.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() {
|
||||
@ -104,10 +107,9 @@ describe('GraphiteQueryCtrl', function() {
|
||||
describe('when initializing a target with single param func using variable', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = 'movingAverage(prod.count, $var)';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl.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() {
|
||||
@ -123,7 +125,7 @@ describe('GraphiteQueryCtrl', function() {
|
||||
describe('when initalizing target without metric expression and function with series-ref', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = 'asPercent(metric.node.count, #A)';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.scope.init();
|
||||
ctx.scope.$digest();
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
@ -141,13 +143,12 @@ describe('GraphiteQueryCtrl', function() {
|
||||
describe('when getting altSegments and metricFindQuery retuns empty array', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.target.target = 'test.count';
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
|
||||
ctx.scope.init();
|
||||
ctx.scope.getAltSegments(1).then(function(results) {
|
||||
ctx.altSegments = results;
|
||||
});
|
||||
ctx.scope.$digest();
|
||||
ctx.scope.$parent = { get_data: sinon.spy() };
|
||||
});
|
||||
|
||||
it('should have no segments', function() {
|
||||
@ -158,11 +159,11 @@ describe('GraphiteQueryCtrl', function() {
|
||||
|
||||
describe('targetChanged', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
|
||||
ctx.panelCtrl.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
|
||||
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 +172,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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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,15 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
queryPart = queryPart.default;
|
||||
|
||||
module.controller('InfluxQueryCtrl', function($scope, templateSrv, $q, uiSegmentSrv) {
|
||||
var panelCtrl = $scope.ctrl;
|
||||
$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, panelCtrl.datasource.database);
|
||||
$scope.groupBySegment = uiSegmentSrv.newPlusButton();
|
||||
$scope.resultFormats = [
|
||||
{text: 'Time series', value: 'time_series'},
|
||||
@ -75,7 +77,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
$scope.getGroupByOptions = function() {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return panelCtrl.datasource.metricFindQuery(query)
|
||||
.then(function(tags) {
|
||||
var options = [];
|
||||
if (!$scope.queryModel.hasFill()) {
|
||||
@ -97,26 +99,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 +132,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 panelCtrl.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 +153,19 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.getMeasurements = function () {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('MEASUREMENTS');
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return panelCtrl.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 panelCtrl.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 panelCtrl.datasource.metricFindQuery(tagsQuery)
|
||||
.then($scope.transformToSegments(true), $scope.handleQueryError);
|
||||
}
|
||||
};
|
||||
@ -211,7 +213,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
addTemplateVars = true;
|
||||
}
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
return panelCtrl.datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments(addTemplateVars))
|
||||
.then(function(results) {
|
||||
if (segment.type === 'key') {
|
||||
@ -224,7 +226,7 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
|
||||
|
||||
$scope.getFieldSegments = function() {
|
||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
|
||||
return $scope.datasource.metricFindQuery(fieldsQuery)
|
||||
return panelCtrl.datasource.metricFindQuery(fieldsQuery)
|
||||
.then($scope.transformToSegments(false))
|
||||
.then(null, $scope.handleQueryError);
|
||||
};
|
||||
@ -234,7 +236,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 +302,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.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();
|
||||
ctx.panelCtrl.datasource = ctx.datasource;
|
||||
ctx.panelCtrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
|
@ -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,43 +0,0 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import PanelMeta from 'app/features/panel/panel_meta2';
|
||||
|
||||
class PanelBaseCtrl {
|
||||
constructor(private $scope) {
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Table',
|
||||
editIcon: "fa fa-table",
|
||||
fullscreen: true,
|
||||
metricsEditor: true,
|
||||
});
|
||||
$scope.testProp = "hello";
|
||||
}
|
||||
}
|
||||
|
||||
class TestPanelCtrl extends PanelBaseCtrl {
|
||||
constructor($scope) {
|
||||
super($scope);
|
||||
$scope.panelMeta.panelName = "Test";
|
||||
}
|
||||
}
|
||||
|
||||
function testPanelDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: `
|
||||
<grafana-panel>
|
||||
<div class="text-center" style="padding-top: 2rem">
|
||||
<h2>Test Panel, {{testProp}}</h2>
|
||||
</div>
|
||||
</grafana-panel>
|
||||
`,
|
||||
controller: TestPanelCtrl,
|
||||
controllerAs: 'ctrl',
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
PanelBaseCtrl,
|
||||
TestPanelCtrl,
|
||||
testPanelDirective as panel
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Test",
|
||||
"id": "test"
|
||||
}
|
@ -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
File diff suppressed because one or more lines are too long
2721
public/vendor/npm/angular2/bundles/testing.dev.js
vendored
2721
public/vendor/npm/angular2/bundles/testing.dev.js
vendored
File diff suppressed because it is too large
Load Diff
774
public/vendor/npm/angular2/bundles/upgrade.dev.js
vendored
774
public/vendor/npm/angular2/bundles/upgrade.dev.js
vendored
@ -1,774 +0,0 @@
|
||||
"format register";
|
||||
System.register("angular2/src/upgrade/metadata", ["angular2/core"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var COMPONENT_SELECTOR = /^[\w|-]*$/;
|
||||
var SKEWER_CASE = /-(\w)/g;
|
||||
var directiveResolver = new core_1.DirectiveResolver();
|
||||
function getComponentInfo(type) {
|
||||
var resolvedMetadata = directiveResolver.resolve(type);
|
||||
var selector = resolvedMetadata.selector;
|
||||
if (!selector.match(COMPONENT_SELECTOR)) {
|
||||
throw new Error('Only selectors matching element names are supported, got: ' + selector);
|
||||
}
|
||||
var selector = selector.replace(SKEWER_CASE, function(all, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
return {
|
||||
type: type,
|
||||
selector: selector,
|
||||
inputs: parseFields(resolvedMetadata.inputs),
|
||||
outputs: parseFields(resolvedMetadata.outputs)
|
||||
};
|
||||
}
|
||||
exports.getComponentInfo = getComponentInfo;
|
||||
function parseFields(names) {
|
||||
var attrProps = [];
|
||||
if (names) {
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
var parts = names[i].split(':');
|
||||
var prop = parts[0].trim();
|
||||
var attr = (parts[1] || parts[0]).trim();
|
||||
var capitalAttr = attr.charAt(0).toUpperCase() + attr.substr(1);
|
||||
attrProps.push({
|
||||
prop: prop,
|
||||
attr: attr,
|
||||
bracketAttr: "[" + attr + "]",
|
||||
parenAttr: "(" + attr + ")",
|
||||
bracketParenAttr: "[(" + attr + ")]",
|
||||
onAttr: "on" + capitalAttr,
|
||||
bindAttr: "bind" + capitalAttr,
|
||||
bindonAttr: "bindon" + capitalAttr
|
||||
});
|
||||
}
|
||||
}
|
||||
return attrProps;
|
||||
}
|
||||
exports.parseFields = parseFields;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/util", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
function stringify(obj) {
|
||||
if (typeof obj == 'function')
|
||||
return obj.name || obj.toString();
|
||||
return '' + obj;
|
||||
}
|
||||
exports.stringify = stringify;
|
||||
function onError(e) {
|
||||
console.log(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
exports.onError = onError;
|
||||
function controllerKey(name) {
|
||||
return '$' + name + 'Controller';
|
||||
}
|
||||
exports.controllerKey = controllerKey;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/constants", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
exports.NG2_APP_VIEW_MANAGER = 'ng2.AppViewManager';
|
||||
exports.NG2_COMPILER = 'ng2.Compiler';
|
||||
exports.NG2_INJECTOR = 'ng2.Injector';
|
||||
exports.NG2_PROTO_VIEW_REF_MAP = 'ng2.ProtoViewRefMap';
|
||||
exports.NG2_ZONE = 'ng2.NgZone';
|
||||
exports.NG1_CONTROLLER = '$controller';
|
||||
exports.NG1_SCOPE = '$scope';
|
||||
exports.NG1_ROOT_SCOPE = '$rootScope';
|
||||
exports.NG1_COMPILE = '$compile';
|
||||
exports.NG1_HTTP_BACKEND = '$httpBackend';
|
||||
exports.NG1_INJECTOR = '$injector';
|
||||
exports.NG1_PARSE = '$parse';
|
||||
exports.NG1_TEMPLATE_CACHE = '$templateCache';
|
||||
exports.REQUIRE_INJECTOR = '^' + exports.NG2_INJECTOR;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/downgrade_ng2_adapter", ["angular2/core", "angular2/src/upgrade/constants"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var INITIAL_VALUE = {__UNINITIALIZED__: true};
|
||||
var DowngradeNg2ComponentAdapter = (function() {
|
||||
function DowngradeNg2ComponentAdapter(id, info, element, attrs, scope, parentInjector, parse, viewManager, protoView) {
|
||||
this.id = id;
|
||||
this.info = info;
|
||||
this.element = element;
|
||||
this.attrs = attrs;
|
||||
this.scope = scope;
|
||||
this.parentInjector = parentInjector;
|
||||
this.parse = parse;
|
||||
this.viewManager = viewManager;
|
||||
this.protoView = protoView;
|
||||
this.component = null;
|
||||
this.inputChangeCount = 0;
|
||||
this.inputChanges = null;
|
||||
this.hostViewRef = null;
|
||||
this.changeDetector = null;
|
||||
this.contentInserctionPoint = null;
|
||||
this.element[0].id = id;
|
||||
this.componentScope = scope.$new();
|
||||
this.childNodes = element.contents();
|
||||
}
|
||||
DowngradeNg2ComponentAdapter.prototype.bootstrapNg2 = function() {
|
||||
var childInjector = this.parentInjector.resolveAndCreateChild([core_1.provide(constants_1.NG1_SCOPE, {useValue: this.componentScope})]);
|
||||
this.hostViewRef = this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
|
||||
var renderer = this.hostViewRef.render;
|
||||
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
|
||||
this.changeDetector = this.hostViewRef.changeDetectorRef;
|
||||
this.component = this.viewManager.getComponent(hostElement);
|
||||
this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.setupInputs = function() {
|
||||
var _this = this;
|
||||
var attrs = this.attrs;
|
||||
var inputs = this.info.inputs;
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var input = inputs[i];
|
||||
var expr = null;
|
||||
if (attrs.hasOwnProperty(input.attr)) {
|
||||
var observeFn = (function(prop) {
|
||||
var prevValue = INITIAL_VALUE;
|
||||
return function(value) {
|
||||
if (_this.inputChanges !== null) {
|
||||
_this.inputChangeCount++;
|
||||
_this.inputChanges[prop] = new Ng1Change(value, prevValue === INITIAL_VALUE ? value : prevValue);
|
||||
prevValue = value;
|
||||
}
|
||||
_this.component[prop] = value;
|
||||
};
|
||||
})(input.prop);
|
||||
attrs.$observe(input.attr, observeFn);
|
||||
} else if (attrs.hasOwnProperty(input.bindAttr)) {
|
||||
expr = attrs[input.bindAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketAttr)) {
|
||||
expr = attrs[input.bracketAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bindonAttr)) {
|
||||
expr = attrs[input.bindonAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
|
||||
expr = attrs[input.bracketParenAttr];
|
||||
}
|
||||
if (expr != null) {
|
||||
var watchFn = (function(prop) {
|
||||
return function(value, prevValue) {
|
||||
if (_this.inputChanges != null) {
|
||||
_this.inputChangeCount++;
|
||||
_this.inputChanges[prop] = new Ng1Change(prevValue, value);
|
||||
}
|
||||
_this.component[prop] = value;
|
||||
};
|
||||
})(input.prop);
|
||||
this.componentScope.$watch(expr, watchFn);
|
||||
}
|
||||
}
|
||||
var prototype = this.info.type.prototype;
|
||||
if (prototype && prototype.ngOnChanges) {
|
||||
this.inputChanges = {};
|
||||
this.componentScope.$watch(function() {
|
||||
return _this.inputChangeCount;
|
||||
}, function() {
|
||||
var inputChanges = _this.inputChanges;
|
||||
_this.inputChanges = {};
|
||||
_this.component.ngOnChanges(inputChanges);
|
||||
});
|
||||
}
|
||||
this.componentScope.$watch(function() {
|
||||
return _this.changeDetector && _this.changeDetector.detectChanges();
|
||||
});
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.projectContent = function() {
|
||||
var childNodes = this.childNodes;
|
||||
if (this.contentInserctionPoint) {
|
||||
var parent = this.contentInserctionPoint.parentNode;
|
||||
for (var i = 0,
|
||||
ii = childNodes.length; i < ii; i++) {
|
||||
parent.insertBefore(childNodes[i], this.contentInserctionPoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.setupOutputs = function() {
|
||||
var _this = this;
|
||||
var attrs = this.attrs;
|
||||
var outputs = this.info.outputs;
|
||||
for (var j = 0; j < outputs.length; j++) {
|
||||
var output = outputs[j];
|
||||
var expr = null;
|
||||
var assignExpr = false;
|
||||
var bindonAttr = output.bindonAttr ? output.bindonAttr.substring(0, output.bindonAttr.length - 6) : null;
|
||||
var bracketParenAttr = output.bracketParenAttr ? "[(" + output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8) + ")]" : null;
|
||||
if (attrs.hasOwnProperty(output.onAttr)) {
|
||||
expr = attrs[output.onAttr];
|
||||
} else if (attrs.hasOwnProperty(output.parenAttr)) {
|
||||
expr = attrs[output.parenAttr];
|
||||
} else if (attrs.hasOwnProperty(bindonAttr)) {
|
||||
expr = attrs[bindonAttr];
|
||||
assignExpr = true;
|
||||
} else if (attrs.hasOwnProperty(bracketParenAttr)) {
|
||||
expr = attrs[bracketParenAttr];
|
||||
assignExpr = true;
|
||||
}
|
||||
if (expr != null && assignExpr != null) {
|
||||
var getter = this.parse(expr);
|
||||
var setter = getter.assign;
|
||||
if (assignExpr && !setter) {
|
||||
throw new Error("Expression '" + expr + "' is not assignable!");
|
||||
}
|
||||
var emitter = this.component[output.prop];
|
||||
if (emitter) {
|
||||
emitter.subscribe({next: assignExpr ? (function(setter) {
|
||||
return function(value) {
|
||||
return setter(_this.scope, value);
|
||||
};
|
||||
})(setter) : (function(getter) {
|
||||
return function(value) {
|
||||
return getter(_this.scope, {$event: value});
|
||||
};
|
||||
})(getter)});
|
||||
} else {
|
||||
throw new Error("Missing emitter '" + output.prop + "' on component '" + this.info.selector + "'!");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.registerCleanup = function() {
|
||||
var _this = this;
|
||||
this.element.bind('$remove', function() {
|
||||
return _this.viewManager.destroyRootHostView(_this.hostViewRef);
|
||||
});
|
||||
};
|
||||
return DowngradeNg2ComponentAdapter;
|
||||
})();
|
||||
exports.DowngradeNg2ComponentAdapter = DowngradeNg2ComponentAdapter;
|
||||
var Ng1Change = (function() {
|
||||
function Ng1Change(previousValue, currentValue) {
|
||||
this.previousValue = previousValue;
|
||||
this.currentValue = currentValue;
|
||||
}
|
||||
Ng1Change.prototype.isFirstChange = function() {
|
||||
return this.previousValue === this.currentValue;
|
||||
};
|
||||
return Ng1Change;
|
||||
})();
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/angular_js", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
function noNg() {
|
||||
throw new Error('AngularJS v1.x is not loaded!');
|
||||
}
|
||||
var angular = {
|
||||
bootstrap: noNg,
|
||||
module: noNg,
|
||||
element: noNg,
|
||||
version: noNg
|
||||
};
|
||||
try {
|
||||
if (window.hasOwnProperty('angular')) {
|
||||
angular = window.angular;
|
||||
}
|
||||
} catch (e) {}
|
||||
exports.bootstrap = angular.bootstrap;
|
||||
exports.module = angular.module;
|
||||
exports.element = angular.element;
|
||||
exports.version = angular.version;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/upgrade_ng1_adapter", ["angular2/core", "angular2/src/upgrade/constants", "angular2/src/upgrade/util", "angular2/src/upgrade/angular_js"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var util_1 = require("angular2/src/upgrade/util");
|
||||
var angular = require("angular2/src/upgrade/angular_js");
|
||||
var CAMEL_CASE = /([A-Z])/g;
|
||||
var INITIAL_VALUE = {__UNINITIALIZED__: true};
|
||||
var NOT_SUPPORTED = 'NOT_SUPPORTED';
|
||||
var UpgradeNg1ComponentAdapterBuilder = (function() {
|
||||
function UpgradeNg1ComponentAdapterBuilder(name) {
|
||||
this.name = name;
|
||||
this.inputs = [];
|
||||
this.inputsRename = [];
|
||||
this.outputs = [];
|
||||
this.outputsRename = [];
|
||||
this.propertyOutputs = [];
|
||||
this.checkProperties = [];
|
||||
this.propertyMap = {};
|
||||
this.linkFn = null;
|
||||
this.directive = null;
|
||||
this.$controller = null;
|
||||
var selector = name.replace(CAMEL_CASE, function(all, next) {
|
||||
return '-' + next.toLowerCase();
|
||||
});
|
||||
var self = this;
|
||||
this.type = core_1.Directive({
|
||||
selector: selector,
|
||||
inputs: this.inputsRename,
|
||||
outputs: this.outputsRename
|
||||
}).Class({
|
||||
constructor: [new core_1.Inject(constants_1.NG1_SCOPE), core_1.ElementRef, function(scope, elementRef) {
|
||||
return new UpgradeNg1ComponentAdapter(self.linkFn, scope, self.directive, elementRef, self.$controller, self.inputs, self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
||||
}],
|
||||
ngOnChanges: function() {},
|
||||
ngDoCheck: function() {}
|
||||
});
|
||||
}
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.extractDirective = function(injector) {
|
||||
var directives = injector.get(this.name + 'Directive');
|
||||
if (directives.length > 1) {
|
||||
throw new Error('Only support single directive definition for: ' + this.name);
|
||||
}
|
||||
var directive = directives[0];
|
||||
if (directive.replace)
|
||||
this.notSupported('replace');
|
||||
if (directive.terminal)
|
||||
this.notSupported('terminal');
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object') {
|
||||
if (link.post)
|
||||
this.notSupported('link.post');
|
||||
}
|
||||
return directive;
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.notSupported = function(feature) {
|
||||
throw new Error("Upgraded directive '" + this.name + "' does not support '" + feature + "'.");
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.extractBindings = function() {
|
||||
var scope = this.directive.scope;
|
||||
if (typeof scope == 'object') {
|
||||
for (var name in scope) {
|
||||
if (scope.hasOwnProperty(name)) {
|
||||
var localName = scope[name];
|
||||
var type = localName.charAt(0);
|
||||
localName = localName.substr(1) || name;
|
||||
var outputName = 'output_' + name;
|
||||
var outputNameRename = outputName + ': ' + name;
|
||||
var outputNameRenameChange = outputName + ': ' + name + 'Change';
|
||||
var inputName = 'input_' + name;
|
||||
var inputNameRename = inputName + ': ' + name;
|
||||
switch (type) {
|
||||
case '=':
|
||||
this.propertyOutputs.push(outputName);
|
||||
this.checkProperties.push(localName);
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRenameChange);
|
||||
this.propertyMap[outputName] = localName;
|
||||
case '@':
|
||||
this.inputs.push(inputName);
|
||||
this.inputsRename.push(inputNameRename);
|
||||
this.propertyMap[inputName] = localName;
|
||||
break;
|
||||
case '&':
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRename);
|
||||
this.propertyMap[outputName] = localName;
|
||||
break;
|
||||
default:
|
||||
var json = JSON.stringify(scope);
|
||||
throw new Error("Unexpected mapping '" + type + "' in '" + json + "' in '" + this.name + "' directive.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.compileTemplate = function(compile, templateCache, httpBackend) {
|
||||
var _this = this;
|
||||
if (this.directive.template !== undefined) {
|
||||
this.linkFn = compileHtml(this.directive.template);
|
||||
} else if (this.directive.templateUrl) {
|
||||
var url = this.directive.templateUrl;
|
||||
var html = templateCache.get(url);
|
||||
if (html !== undefined) {
|
||||
this.linkFn = compileHtml(html);
|
||||
} else {
|
||||
return new Promise(function(resolve, err) {
|
||||
httpBackend('GET', url, null, function(status, response) {
|
||||
if (status == 200) {
|
||||
resolve(_this.linkFn = compileHtml(templateCache.put(url, response)));
|
||||
} else {
|
||||
err("GET " + url + " returned " + status + ": " + response);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error("Directive '" + this.name + "' is not a component, it is missing template.");
|
||||
}
|
||||
return null;
|
||||
function compileHtml(html) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return compile(div.childNodes);
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.resolve = function(exportedComponents, injector) {
|
||||
var promises = [];
|
||||
var compile = injector.get(constants_1.NG1_COMPILE);
|
||||
var templateCache = injector.get(constants_1.NG1_TEMPLATE_CACHE);
|
||||
var httpBackend = injector.get(constants_1.NG1_HTTP_BACKEND);
|
||||
var $controller = injector.get(constants_1.NG1_CONTROLLER);
|
||||
for (var name in exportedComponents) {
|
||||
if (exportedComponents.hasOwnProperty(name)) {
|
||||
var exportedComponent = exportedComponents[name];
|
||||
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
||||
exportedComponent.$controller = $controller;
|
||||
exportedComponent.extractBindings();
|
||||
var promise = exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
||||
if (promise)
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
};
|
||||
return UpgradeNg1ComponentAdapterBuilder;
|
||||
})();
|
||||
exports.UpgradeNg1ComponentAdapterBuilder = UpgradeNg1ComponentAdapterBuilder;
|
||||
var UpgradeNg1ComponentAdapter = (function() {
|
||||
function UpgradeNg1ComponentAdapter(linkFn, scope, directive, elementRef, $controller, inputs, outputs, propOuts, checkProperties, propertyMap) {
|
||||
this.directive = directive;
|
||||
this.inputs = inputs;
|
||||
this.outputs = outputs;
|
||||
this.propOuts = propOuts;
|
||||
this.checkProperties = checkProperties;
|
||||
this.propertyMap = propertyMap;
|
||||
this.destinationObj = null;
|
||||
this.checkLastValues = [];
|
||||
var element = elementRef.nativeElement;
|
||||
var childNodes = [];
|
||||
var childNode;
|
||||
while (childNode = element.firstChild) {
|
||||
element.removeChild(childNode);
|
||||
childNodes.push(childNode);
|
||||
}
|
||||
var componentScope = scope.$new(!!directive.scope);
|
||||
var $element = angular.element(element);
|
||||
var controllerType = directive.controller;
|
||||
var controller = null;
|
||||
if (controllerType) {
|
||||
var locals = {
|
||||
$scope: componentScope,
|
||||
$element: $element
|
||||
};
|
||||
controller = $controller(controllerType, locals, null, directive.controllerAs);
|
||||
$element.data(util_1.controllerKey(directive.name), controller);
|
||||
}
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object')
|
||||
link = link.pre;
|
||||
if (link) {
|
||||
var attrs = NOT_SUPPORTED;
|
||||
var transcludeFn = NOT_SUPPORTED;
|
||||
var linkController = this.resolveRequired($element, directive.require);
|
||||
directive.link(componentScope, $element, attrs, linkController, transcludeFn);
|
||||
}
|
||||
this.destinationObj = directive.bindToController && controller ? controller : componentScope;
|
||||
linkFn(componentScope, function(clonedElement, scope) {
|
||||
for (var i = 0,
|
||||
ii = clonedElement.length; i < ii; i++) {
|
||||
element.appendChild(clonedElement[i]);
|
||||
}
|
||||
}, {parentBoundTranscludeFn: function(scope, cloneAttach) {
|
||||
cloneAttach(childNodes);
|
||||
}});
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
this[inputs[i]] = null;
|
||||
}
|
||||
for (var j = 0; j < outputs.length; j++) {
|
||||
var emitter = this[outputs[j]] = new core_1.EventEmitter();
|
||||
this.setComponentProperty(outputs[j], (function(emitter) {
|
||||
return function(value) {
|
||||
return emitter.emit(value);
|
||||
};
|
||||
})(emitter));
|
||||
}
|
||||
for (var k = 0; k < propOuts.length; k++) {
|
||||
this[propOuts[k]] = new core_1.EventEmitter();
|
||||
this.checkLastValues.push(INITIAL_VALUE);
|
||||
}
|
||||
}
|
||||
UpgradeNg1ComponentAdapter.prototype.ngOnChanges = function(changes) {
|
||||
for (var name in changes) {
|
||||
if (changes.hasOwnProperty(name)) {
|
||||
var change = changes[name];
|
||||
this.setComponentProperty(name, change.currentValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.ngDoCheck = function() {
|
||||
var count = 0;
|
||||
var destinationObj = this.destinationObj;
|
||||
var lastValues = this.checkLastValues;
|
||||
var checkProperties = this.checkProperties;
|
||||
for (var i = 0; i < checkProperties.length; i++) {
|
||||
var value = destinationObj[checkProperties[i]];
|
||||
var last = lastValues[i];
|
||||
if (value !== last) {
|
||||
if (typeof value == 'number' && isNaN(value) && typeof last == 'number' && isNaN(last)) {} else {
|
||||
var eventEmitter = this[this.propOuts[i]];
|
||||
eventEmitter.emit(lastValues[i] = value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.setComponentProperty = function(name, value) {
|
||||
this.destinationObj[this.propertyMap[name]] = value;
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.resolveRequired = function($element, require) {
|
||||
if (!require) {
|
||||
return undefined;
|
||||
} else if (typeof require == 'string') {
|
||||
var name = require;
|
||||
var isOptional = false;
|
||||
var startParent = false;
|
||||
var searchParents = false;
|
||||
var ch;
|
||||
if (name.charAt(0) == '?') {
|
||||
isOptional = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
searchParents = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
startParent = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
var key = util_1.controllerKey(name);
|
||||
if (startParent)
|
||||
$element = $element.parent();
|
||||
var dep = searchParents ? $element.inheritedData(key) : $element.data(key);
|
||||
if (!dep && !isOptional) {
|
||||
throw new Error("Can not locate '" + require + "' in '" + this.directive.name + "'.");
|
||||
}
|
||||
return dep;
|
||||
} else if (require instanceof Array) {
|
||||
var deps = [];
|
||||
for (var i = 0; i < require.length; i++) {
|
||||
deps.push(this.resolveRequired($element, require[i]));
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
throw new Error("Directive '" + this.directive.name + "' require syntax unrecognized: " + this.directive.require);
|
||||
};
|
||||
return UpgradeNg1ComponentAdapter;
|
||||
})();
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/upgrade_adapter", ["angular2/core", "angular2/src/facade/async", "angular2/platform/browser", "angular2/src/upgrade/metadata", "angular2/src/upgrade/util", "angular2/src/upgrade/constants", "angular2/src/upgrade/downgrade_ng2_adapter", "angular2/src/upgrade/upgrade_ng1_adapter", "angular2/src/upgrade/angular_js"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var async_1 = require("angular2/src/facade/async");
|
||||
var browser_1 = require("angular2/platform/browser");
|
||||
var metadata_1 = require("angular2/src/upgrade/metadata");
|
||||
var util_1 = require("angular2/src/upgrade/util");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var downgrade_ng2_adapter_1 = require("angular2/src/upgrade/downgrade_ng2_adapter");
|
||||
var upgrade_ng1_adapter_1 = require("angular2/src/upgrade/upgrade_ng1_adapter");
|
||||
var angular = require("angular2/src/upgrade/angular_js");
|
||||
var upgradeCount = 0;
|
||||
var UpgradeAdapter = (function() {
|
||||
function UpgradeAdapter() {
|
||||
this.idPrefix = "NG2_UPGRADE_" + upgradeCount++ + "_";
|
||||
this.upgradedComponents = [];
|
||||
this.downgradedComponents = {};
|
||||
this.providers = [];
|
||||
}
|
||||
UpgradeAdapter.prototype.downgradeNg2Component = function(type) {
|
||||
this.upgradedComponents.push(type);
|
||||
var info = metadata_1.getComponentInfo(type);
|
||||
return ng1ComponentDirective(info, "" + this.idPrefix + info.selector + "_c");
|
||||
};
|
||||
UpgradeAdapter.prototype.upgradeNg1Component = function(name) {
|
||||
if (this.downgradedComponents.hasOwnProperty(name)) {
|
||||
return this.downgradedComponents[name].type;
|
||||
} else {
|
||||
return (this.downgradedComponents[name] = new upgrade_ng1_adapter_1.UpgradeNg1ComponentAdapterBuilder(name)).type;
|
||||
}
|
||||
};
|
||||
UpgradeAdapter.prototype.bootstrap = function(element, modules, config) {
|
||||
var _this = this;
|
||||
var upgrade = new UpgradeAdapterRef();
|
||||
var ng1Injector = null;
|
||||
var platformRef = core_1.platform(browser_1.BROWSER_PROVIDERS);
|
||||
var applicationRef = platformRef.application([browser_1.BROWSER_APP_PROVIDERS, core_1.provide(constants_1.NG1_INJECTOR, {useFactory: function() {
|
||||
return ng1Injector;
|
||||
}}), core_1.provide(constants_1.NG1_COMPILE, {useFactory: function() {
|
||||
return ng1Injector.get(constants_1.NG1_COMPILE);
|
||||
}}), this.providers]);
|
||||
var injector = applicationRef.injector;
|
||||
var ngZone = injector.get(core_1.NgZone);
|
||||
var compiler = injector.get(core_1.Compiler);
|
||||
var delayApplyExps = [];
|
||||
var original$applyFn;
|
||||
var rootScopePrototype;
|
||||
var rootScope;
|
||||
var protoViewRefMap = {};
|
||||
var ng1Module = angular.module(this.idPrefix, modules);
|
||||
var ng1compilePromise = null;
|
||||
ng1Module.value(constants_1.NG2_INJECTOR, injector).value(constants_1.NG2_ZONE, ngZone).value(constants_1.NG2_COMPILER, compiler).value(constants_1.NG2_PROTO_VIEW_REF_MAP, protoViewRefMap).value(constants_1.NG2_APP_VIEW_MANAGER, injector.get(core_1.AppViewManager)).config(['$provide', function(provide) {
|
||||
provide.decorator(constants_1.NG1_ROOT_SCOPE, ['$delegate', function(rootScopeDelegate) {
|
||||
rootScopePrototype = rootScopeDelegate.constructor.prototype;
|
||||
if (rootScopePrototype.hasOwnProperty('$apply')) {
|
||||
original$applyFn = rootScopePrototype.$apply;
|
||||
rootScopePrototype.$apply = function(exp) {
|
||||
return delayApplyExps.push(exp);
|
||||
};
|
||||
} else {
|
||||
throw new Error("Failed to find '$apply' on '$rootScope'!");
|
||||
}
|
||||
return rootScope = rootScopeDelegate;
|
||||
}]);
|
||||
}]).run(['$injector', '$rootScope', function(injector, rootScope) {
|
||||
ng1Injector = injector;
|
||||
async_1.ObservableWrapper.subscribe(ngZone.onTurnDone, function(_) {
|
||||
ngZone.run(function() {
|
||||
return rootScope.$apply();
|
||||
});
|
||||
});
|
||||
ng1compilePromise = upgrade_ng1_adapter_1.UpgradeNg1ComponentAdapterBuilder.resolve(_this.downgradedComponents, injector);
|
||||
}]);
|
||||
angular.element(element).data(util_1.controllerKey(constants_1.NG2_INJECTOR), injector);
|
||||
ngZone.run(function() {
|
||||
angular.bootstrap(element, [_this.idPrefix], config);
|
||||
});
|
||||
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise]).then(function() {
|
||||
ngZone.run(function() {
|
||||
if (rootScopePrototype) {
|
||||
rootScopePrototype.$apply = original$applyFn;
|
||||
while (delayApplyExps.length) {
|
||||
rootScope.$apply(delayApplyExps.shift());
|
||||
}
|
||||
upgrade._bootstrapDone(applicationRef, ng1Injector);
|
||||
rootScopePrototype = null;
|
||||
}
|
||||
});
|
||||
}, util_1.onError);
|
||||
return upgrade;
|
||||
};
|
||||
UpgradeAdapter.prototype.addProvider = function(provider) {
|
||||
this.providers.push(provider);
|
||||
};
|
||||
UpgradeAdapter.prototype.upgradeNg1Provider = function(name, options) {
|
||||
var token = options && options.asToken || name;
|
||||
this.providers.push(core_1.provide(token, {
|
||||
useFactory: function(ng1Injector) {
|
||||
return ng1Injector.get(name);
|
||||
},
|
||||
deps: [constants_1.NG1_INJECTOR]
|
||||
}));
|
||||
};
|
||||
UpgradeAdapter.prototype.downgradeNg2Provider = function(token) {
|
||||
var factory = function(injector) {
|
||||
return injector.get(token);
|
||||
};
|
||||
factory.$inject = [constants_1.NG2_INJECTOR];
|
||||
return factory;
|
||||
};
|
||||
UpgradeAdapter.prototype.compileNg2Components = function(compiler, protoViewRefMap) {
|
||||
var _this = this;
|
||||
var promises = [];
|
||||
var types = this.upgradedComponents;
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
promises.push(compiler.compileInHost(types[i]));
|
||||
}
|
||||
return Promise.all(promises).then(function(protoViews) {
|
||||
var types = _this.upgradedComponents;
|
||||
for (var i = 0; i < protoViews.length; i++) {
|
||||
protoViewRefMap[metadata_1.getComponentInfo(types[i]).selector] = protoViews[i];
|
||||
}
|
||||
return protoViewRefMap;
|
||||
}, util_1.onError);
|
||||
};
|
||||
return UpgradeAdapter;
|
||||
})();
|
||||
exports.UpgradeAdapter = UpgradeAdapter;
|
||||
function ng1ComponentDirective(info, idPrefix) {
|
||||
directiveFactory.$inject = [constants_1.NG2_PROTO_VIEW_REF_MAP, constants_1.NG2_APP_VIEW_MANAGER, constants_1.NG1_PARSE];
|
||||
function directiveFactory(protoViewRefMap, viewManager, parse) {
|
||||
var protoView = protoViewRefMap[info.selector];
|
||||
if (!protoView)
|
||||
throw new Error('Expecting ProtoViewRef for: ' + info.selector);
|
||||
var idCount = 0;
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: constants_1.REQUIRE_INJECTOR,
|
||||
link: {post: function(scope, element, attrs, parentInjector, transclude) {
|
||||
var domElement = element[0];
|
||||
var facade = new downgrade_ng2_adapter_1.DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element, attrs, scope, parentInjector, parse, viewManager, protoView);
|
||||
facade.setupInputs();
|
||||
facade.bootstrapNg2();
|
||||
facade.projectContent();
|
||||
facade.setupOutputs();
|
||||
facade.registerCleanup();
|
||||
}}
|
||||
};
|
||||
}
|
||||
return directiveFactory;
|
||||
}
|
||||
var UpgradeAdapterRef = (function() {
|
||||
function UpgradeAdapterRef() {
|
||||
this._readyFn = null;
|
||||
this.ng1RootScope = null;
|
||||
this.ng1Injector = null;
|
||||
this.ng2ApplicationRef = null;
|
||||
this.ng2Injector = null;
|
||||
}
|
||||
UpgradeAdapterRef.prototype._bootstrapDone = function(applicationRef, ng1Injector) {
|
||||
this.ng2ApplicationRef = applicationRef;
|
||||
this.ng2Injector = applicationRef.injector;
|
||||
this.ng1Injector = ng1Injector;
|
||||
this.ng1RootScope = ng1Injector.get(constants_1.NG1_ROOT_SCOPE);
|
||||
this._readyFn && this._readyFn(this);
|
||||
};
|
||||
UpgradeAdapterRef.prototype.ready = function(fn) {
|
||||
this._readyFn = fn;
|
||||
};
|
||||
UpgradeAdapterRef.prototype.dispose = function() {
|
||||
this.ng1Injector.get(constants_1.NG1_ROOT_SCOPE).$destroy();
|
||||
this.ng2ApplicationRef.dispose();
|
||||
};
|
||||
return UpgradeAdapterRef;
|
||||
})();
|
||||
exports.UpgradeAdapterRef = UpgradeAdapterRef;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/upgrade", ["angular2/src/upgrade/upgrade_adapter"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var upgrade_adapter_1 = require("angular2/src/upgrade/upgrade_adapter");
|
||||
exports.UpgradeAdapter = upgrade_adapter_1.UpgradeAdapter;
|
||||
exports.UpgradeAdapterRef = upgrade_adapter_1.UpgradeAdapterRef;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
//# sourceMappingURLDisabled=upgrade.dev.js.map
|
774
public/vendor/npm/angular2/bundles/upgrade.js
vendored
774
public/vendor/npm/angular2/bundles/upgrade.js
vendored
@ -1,774 +0,0 @@
|
||||
"format register";
|
||||
System.register("angular2/src/upgrade/metadata", ["angular2/core"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var COMPONENT_SELECTOR = /^[\w|-]*$/;
|
||||
var SKEWER_CASE = /-(\w)/g;
|
||||
var directiveResolver = new core_1.DirectiveResolver();
|
||||
function getComponentInfo(type) {
|
||||
var resolvedMetadata = directiveResolver.resolve(type);
|
||||
var selector = resolvedMetadata.selector;
|
||||
if (!selector.match(COMPONENT_SELECTOR)) {
|
||||
throw new Error('Only selectors matching element names are supported, got: ' + selector);
|
||||
}
|
||||
var selector = selector.replace(SKEWER_CASE, function(all, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
return {
|
||||
type: type,
|
||||
selector: selector,
|
||||
inputs: parseFields(resolvedMetadata.inputs),
|
||||
outputs: parseFields(resolvedMetadata.outputs)
|
||||
};
|
||||
}
|
||||
exports.getComponentInfo = getComponentInfo;
|
||||
function parseFields(names) {
|
||||
var attrProps = [];
|
||||
if (names) {
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
var parts = names[i].split(':');
|
||||
var prop = parts[0].trim();
|
||||
var attr = (parts[1] || parts[0]).trim();
|
||||
var capitalAttr = attr.charAt(0).toUpperCase() + attr.substr(1);
|
||||
attrProps.push({
|
||||
prop: prop,
|
||||
attr: attr,
|
||||
bracketAttr: "[" + attr + "]",
|
||||
parenAttr: "(" + attr + ")",
|
||||
bracketParenAttr: "[(" + attr + ")]",
|
||||
onAttr: "on" + capitalAttr,
|
||||
bindAttr: "bind" + capitalAttr,
|
||||
bindonAttr: "bindon" + capitalAttr
|
||||
});
|
||||
}
|
||||
}
|
||||
return attrProps;
|
||||
}
|
||||
exports.parseFields = parseFields;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/util", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
function stringify(obj) {
|
||||
if (typeof obj == 'function')
|
||||
return obj.name || obj.toString();
|
||||
return '' + obj;
|
||||
}
|
||||
exports.stringify = stringify;
|
||||
function onError(e) {
|
||||
console.log(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
exports.onError = onError;
|
||||
function controllerKey(name) {
|
||||
return '$' + name + 'Controller';
|
||||
}
|
||||
exports.controllerKey = controllerKey;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/constants", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
exports.NG2_APP_VIEW_MANAGER = 'ng2.AppViewManager';
|
||||
exports.NG2_COMPILER = 'ng2.Compiler';
|
||||
exports.NG2_INJECTOR = 'ng2.Injector';
|
||||
exports.NG2_PROTO_VIEW_REF_MAP = 'ng2.ProtoViewRefMap';
|
||||
exports.NG2_ZONE = 'ng2.NgZone';
|
||||
exports.NG1_CONTROLLER = '$controller';
|
||||
exports.NG1_SCOPE = '$scope';
|
||||
exports.NG1_ROOT_SCOPE = '$rootScope';
|
||||
exports.NG1_COMPILE = '$compile';
|
||||
exports.NG1_HTTP_BACKEND = '$httpBackend';
|
||||
exports.NG1_INJECTOR = '$injector';
|
||||
exports.NG1_PARSE = '$parse';
|
||||
exports.NG1_TEMPLATE_CACHE = '$templateCache';
|
||||
exports.REQUIRE_INJECTOR = '^' + exports.NG2_INJECTOR;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/downgrade_ng2_adapter", ["angular2/core", "angular2/src/upgrade/constants"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var INITIAL_VALUE = {__UNINITIALIZED__: true};
|
||||
var DowngradeNg2ComponentAdapter = (function() {
|
||||
function DowngradeNg2ComponentAdapter(id, info, element, attrs, scope, parentInjector, parse, viewManager, protoView) {
|
||||
this.id = id;
|
||||
this.info = info;
|
||||
this.element = element;
|
||||
this.attrs = attrs;
|
||||
this.scope = scope;
|
||||
this.parentInjector = parentInjector;
|
||||
this.parse = parse;
|
||||
this.viewManager = viewManager;
|
||||
this.protoView = protoView;
|
||||
this.component = null;
|
||||
this.inputChangeCount = 0;
|
||||
this.inputChanges = null;
|
||||
this.hostViewRef = null;
|
||||
this.changeDetector = null;
|
||||
this.contentInserctionPoint = null;
|
||||
this.element[0].id = id;
|
||||
this.componentScope = scope.$new();
|
||||
this.childNodes = element.contents();
|
||||
}
|
||||
DowngradeNg2ComponentAdapter.prototype.bootstrapNg2 = function() {
|
||||
var childInjector = this.parentInjector.resolveAndCreateChild([core_1.provide(constants_1.NG1_SCOPE, {useValue: this.componentScope})]);
|
||||
this.hostViewRef = this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
|
||||
var renderer = this.hostViewRef.render;
|
||||
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
|
||||
this.changeDetector = this.hostViewRef.changeDetectorRef;
|
||||
this.component = this.viewManager.getComponent(hostElement);
|
||||
this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.setupInputs = function() {
|
||||
var _this = this;
|
||||
var attrs = this.attrs;
|
||||
var inputs = this.info.inputs;
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var input = inputs[i];
|
||||
var expr = null;
|
||||
if (attrs.hasOwnProperty(input.attr)) {
|
||||
var observeFn = (function(prop) {
|
||||
var prevValue = INITIAL_VALUE;
|
||||
return function(value) {
|
||||
if (_this.inputChanges !== null) {
|
||||
_this.inputChangeCount++;
|
||||
_this.inputChanges[prop] = new Ng1Change(value, prevValue === INITIAL_VALUE ? value : prevValue);
|
||||
prevValue = value;
|
||||
}
|
||||
_this.component[prop] = value;
|
||||
};
|
||||
})(input.prop);
|
||||
attrs.$observe(input.attr, observeFn);
|
||||
} else if (attrs.hasOwnProperty(input.bindAttr)) {
|
||||
expr = attrs[input.bindAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketAttr)) {
|
||||
expr = attrs[input.bracketAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bindonAttr)) {
|
||||
expr = attrs[input.bindonAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
|
||||
expr = attrs[input.bracketParenAttr];
|
||||
}
|
||||
if (expr != null) {
|
||||
var watchFn = (function(prop) {
|
||||
return function(value, prevValue) {
|
||||
if (_this.inputChanges != null) {
|
||||
_this.inputChangeCount++;
|
||||
_this.inputChanges[prop] = new Ng1Change(prevValue, value);
|
||||
}
|
||||
_this.component[prop] = value;
|
||||
};
|
||||
})(input.prop);
|
||||
this.componentScope.$watch(expr, watchFn);
|
||||
}
|
||||
}
|
||||
var prototype = this.info.type.prototype;
|
||||
if (prototype && prototype.ngOnChanges) {
|
||||
this.inputChanges = {};
|
||||
this.componentScope.$watch(function() {
|
||||
return _this.inputChangeCount;
|
||||
}, function() {
|
||||
var inputChanges = _this.inputChanges;
|
||||
_this.inputChanges = {};
|
||||
_this.component.ngOnChanges(inputChanges);
|
||||
});
|
||||
}
|
||||
this.componentScope.$watch(function() {
|
||||
return _this.changeDetector && _this.changeDetector.detectChanges();
|
||||
});
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.projectContent = function() {
|
||||
var childNodes = this.childNodes;
|
||||
if (this.contentInserctionPoint) {
|
||||
var parent = this.contentInserctionPoint.parentNode;
|
||||
for (var i = 0,
|
||||
ii = childNodes.length; i < ii; i++) {
|
||||
parent.insertBefore(childNodes[i], this.contentInserctionPoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.setupOutputs = function() {
|
||||
var _this = this;
|
||||
var attrs = this.attrs;
|
||||
var outputs = this.info.outputs;
|
||||
for (var j = 0; j < outputs.length; j++) {
|
||||
var output = outputs[j];
|
||||
var expr = null;
|
||||
var assignExpr = false;
|
||||
var bindonAttr = output.bindonAttr ? output.bindonAttr.substring(0, output.bindonAttr.length - 6) : null;
|
||||
var bracketParenAttr = output.bracketParenAttr ? "[(" + output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8) + ")]" : null;
|
||||
if (attrs.hasOwnProperty(output.onAttr)) {
|
||||
expr = attrs[output.onAttr];
|
||||
} else if (attrs.hasOwnProperty(output.parenAttr)) {
|
||||
expr = attrs[output.parenAttr];
|
||||
} else if (attrs.hasOwnProperty(bindonAttr)) {
|
||||
expr = attrs[bindonAttr];
|
||||
assignExpr = true;
|
||||
} else if (attrs.hasOwnProperty(bracketParenAttr)) {
|
||||
expr = attrs[bracketParenAttr];
|
||||
assignExpr = true;
|
||||
}
|
||||
if (expr != null && assignExpr != null) {
|
||||
var getter = this.parse(expr);
|
||||
var setter = getter.assign;
|
||||
if (assignExpr && !setter) {
|
||||
throw new Error("Expression '" + expr + "' is not assignable!");
|
||||
}
|
||||
var emitter = this.component[output.prop];
|
||||
if (emitter) {
|
||||
emitter.subscribe({next: assignExpr ? (function(setter) {
|
||||
return function(value) {
|
||||
return setter(_this.scope, value);
|
||||
};
|
||||
})(setter) : (function(getter) {
|
||||
return function(value) {
|
||||
return getter(_this.scope, {$event: value});
|
||||
};
|
||||
})(getter)});
|
||||
} else {
|
||||
throw new Error("Missing emitter '" + output.prop + "' on component '" + this.info.selector + "'!");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DowngradeNg2ComponentAdapter.prototype.registerCleanup = function() {
|
||||
var _this = this;
|
||||
this.element.bind('$remove', function() {
|
||||
return _this.viewManager.destroyRootHostView(_this.hostViewRef);
|
||||
});
|
||||
};
|
||||
return DowngradeNg2ComponentAdapter;
|
||||
})();
|
||||
exports.DowngradeNg2ComponentAdapter = DowngradeNg2ComponentAdapter;
|
||||
var Ng1Change = (function() {
|
||||
function Ng1Change(previousValue, currentValue) {
|
||||
this.previousValue = previousValue;
|
||||
this.currentValue = currentValue;
|
||||
}
|
||||
Ng1Change.prototype.isFirstChange = function() {
|
||||
return this.previousValue === this.currentValue;
|
||||
};
|
||||
return Ng1Change;
|
||||
})();
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/angular_js", [], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
function noNg() {
|
||||
throw new Error('AngularJS v1.x is not loaded!');
|
||||
}
|
||||
var angular = {
|
||||
bootstrap: noNg,
|
||||
module: noNg,
|
||||
element: noNg,
|
||||
version: noNg
|
||||
};
|
||||
try {
|
||||
if (window.hasOwnProperty('angular')) {
|
||||
angular = window.angular;
|
||||
}
|
||||
} catch (e) {}
|
||||
exports.bootstrap = angular.bootstrap;
|
||||
exports.module = angular.module;
|
||||
exports.element = angular.element;
|
||||
exports.version = angular.version;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/upgrade_ng1_adapter", ["angular2/core", "angular2/src/upgrade/constants", "angular2/src/upgrade/util", "angular2/src/upgrade/angular_js"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var util_1 = require("angular2/src/upgrade/util");
|
||||
var angular = require("angular2/src/upgrade/angular_js");
|
||||
var CAMEL_CASE = /([A-Z])/g;
|
||||
var INITIAL_VALUE = {__UNINITIALIZED__: true};
|
||||
var NOT_SUPPORTED = 'NOT_SUPPORTED';
|
||||
var UpgradeNg1ComponentAdapterBuilder = (function() {
|
||||
function UpgradeNg1ComponentAdapterBuilder(name) {
|
||||
this.name = name;
|
||||
this.inputs = [];
|
||||
this.inputsRename = [];
|
||||
this.outputs = [];
|
||||
this.outputsRename = [];
|
||||
this.propertyOutputs = [];
|
||||
this.checkProperties = [];
|
||||
this.propertyMap = {};
|
||||
this.linkFn = null;
|
||||
this.directive = null;
|
||||
this.$controller = null;
|
||||
var selector = name.replace(CAMEL_CASE, function(all, next) {
|
||||
return '-' + next.toLowerCase();
|
||||
});
|
||||
var self = this;
|
||||
this.type = core_1.Directive({
|
||||
selector: selector,
|
||||
inputs: this.inputsRename,
|
||||
outputs: this.outputsRename
|
||||
}).Class({
|
||||
constructor: [new core_1.Inject(constants_1.NG1_SCOPE), core_1.ElementRef, function(scope, elementRef) {
|
||||
return new UpgradeNg1ComponentAdapter(self.linkFn, scope, self.directive, elementRef, self.$controller, self.inputs, self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
||||
}],
|
||||
ngOnChanges: function() {},
|
||||
ngDoCheck: function() {}
|
||||
});
|
||||
}
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.extractDirective = function(injector) {
|
||||
var directives = injector.get(this.name + 'Directive');
|
||||
if (directives.length > 1) {
|
||||
throw new Error('Only support single directive definition for: ' + this.name);
|
||||
}
|
||||
var directive = directives[0];
|
||||
if (directive.replace)
|
||||
this.notSupported('replace');
|
||||
if (directive.terminal)
|
||||
this.notSupported('terminal');
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object') {
|
||||
if (link.post)
|
||||
this.notSupported('link.post');
|
||||
}
|
||||
return directive;
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.notSupported = function(feature) {
|
||||
throw new Error("Upgraded directive '" + this.name + "' does not support '" + feature + "'.");
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.extractBindings = function() {
|
||||
var scope = this.directive.scope;
|
||||
if (typeof scope == 'object') {
|
||||
for (var name in scope) {
|
||||
if (scope.hasOwnProperty(name)) {
|
||||
var localName = scope[name];
|
||||
var type = localName.charAt(0);
|
||||
localName = localName.substr(1) || name;
|
||||
var outputName = 'output_' + name;
|
||||
var outputNameRename = outputName + ': ' + name;
|
||||
var outputNameRenameChange = outputName + ': ' + name + 'Change';
|
||||
var inputName = 'input_' + name;
|
||||
var inputNameRename = inputName + ': ' + name;
|
||||
switch (type) {
|
||||
case '=':
|
||||
this.propertyOutputs.push(outputName);
|
||||
this.checkProperties.push(localName);
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRenameChange);
|
||||
this.propertyMap[outputName] = localName;
|
||||
case '@':
|
||||
this.inputs.push(inputName);
|
||||
this.inputsRename.push(inputNameRename);
|
||||
this.propertyMap[inputName] = localName;
|
||||
break;
|
||||
case '&':
|
||||
this.outputs.push(outputName);
|
||||
this.outputsRename.push(outputNameRename);
|
||||
this.propertyMap[outputName] = localName;
|
||||
break;
|
||||
default:
|
||||
var json = JSON.stringify(scope);
|
||||
throw new Error("Unexpected mapping '" + type + "' in '" + json + "' in '" + this.name + "' directive.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.prototype.compileTemplate = function(compile, templateCache, httpBackend) {
|
||||
var _this = this;
|
||||
if (this.directive.template !== undefined) {
|
||||
this.linkFn = compileHtml(this.directive.template);
|
||||
} else if (this.directive.templateUrl) {
|
||||
var url = this.directive.templateUrl;
|
||||
var html = templateCache.get(url);
|
||||
if (html !== undefined) {
|
||||
this.linkFn = compileHtml(html);
|
||||
} else {
|
||||
return new Promise(function(resolve, err) {
|
||||
httpBackend('GET', url, null, function(status, response) {
|
||||
if (status == 200) {
|
||||
resolve(_this.linkFn = compileHtml(templateCache.put(url, response)));
|
||||
} else {
|
||||
err("GET " + url + " returned " + status + ": " + response);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error("Directive '" + this.name + "' is not a component, it is missing template.");
|
||||
}
|
||||
return null;
|
||||
function compileHtml(html) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return compile(div.childNodes);
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapterBuilder.resolve = function(exportedComponents, injector) {
|
||||
var promises = [];
|
||||
var compile = injector.get(constants_1.NG1_COMPILE);
|
||||
var templateCache = injector.get(constants_1.NG1_TEMPLATE_CACHE);
|
||||
var httpBackend = injector.get(constants_1.NG1_HTTP_BACKEND);
|
||||
var $controller = injector.get(constants_1.NG1_CONTROLLER);
|
||||
for (var name in exportedComponents) {
|
||||
if (exportedComponents.hasOwnProperty(name)) {
|
||||
var exportedComponent = exportedComponents[name];
|
||||
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
||||
exportedComponent.$controller = $controller;
|
||||
exportedComponent.extractBindings();
|
||||
var promise = exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
||||
if (promise)
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
};
|
||||
return UpgradeNg1ComponentAdapterBuilder;
|
||||
})();
|
||||
exports.UpgradeNg1ComponentAdapterBuilder = UpgradeNg1ComponentAdapterBuilder;
|
||||
var UpgradeNg1ComponentAdapter = (function() {
|
||||
function UpgradeNg1ComponentAdapter(linkFn, scope, directive, elementRef, $controller, inputs, outputs, propOuts, checkProperties, propertyMap) {
|
||||
this.directive = directive;
|
||||
this.inputs = inputs;
|
||||
this.outputs = outputs;
|
||||
this.propOuts = propOuts;
|
||||
this.checkProperties = checkProperties;
|
||||
this.propertyMap = propertyMap;
|
||||
this.destinationObj = null;
|
||||
this.checkLastValues = [];
|
||||
var element = elementRef.nativeElement;
|
||||
var childNodes = [];
|
||||
var childNode;
|
||||
while (childNode = element.firstChild) {
|
||||
element.removeChild(childNode);
|
||||
childNodes.push(childNode);
|
||||
}
|
||||
var componentScope = scope.$new(!!directive.scope);
|
||||
var $element = angular.element(element);
|
||||
var controllerType = directive.controller;
|
||||
var controller = null;
|
||||
if (controllerType) {
|
||||
var locals = {
|
||||
$scope: componentScope,
|
||||
$element: $element
|
||||
};
|
||||
controller = $controller(controllerType, locals, null, directive.controllerAs);
|
||||
$element.data(util_1.controllerKey(directive.name), controller);
|
||||
}
|
||||
var link = directive.link;
|
||||
if (typeof link == 'object')
|
||||
link = link.pre;
|
||||
if (link) {
|
||||
var attrs = NOT_SUPPORTED;
|
||||
var transcludeFn = NOT_SUPPORTED;
|
||||
var linkController = this.resolveRequired($element, directive.require);
|
||||
directive.link(componentScope, $element, attrs, linkController, transcludeFn);
|
||||
}
|
||||
this.destinationObj = directive.bindToController && controller ? controller : componentScope;
|
||||
linkFn(componentScope, function(clonedElement, scope) {
|
||||
for (var i = 0,
|
||||
ii = clonedElement.length; i < ii; i++) {
|
||||
element.appendChild(clonedElement[i]);
|
||||
}
|
||||
}, {parentBoundTranscludeFn: function(scope, cloneAttach) {
|
||||
cloneAttach(childNodes);
|
||||
}});
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
this[inputs[i]] = null;
|
||||
}
|
||||
for (var j = 0; j < outputs.length; j++) {
|
||||
var emitter = this[outputs[j]] = new core_1.EventEmitter();
|
||||
this.setComponentProperty(outputs[j], (function(emitter) {
|
||||
return function(value) {
|
||||
return emitter.emit(value);
|
||||
};
|
||||
})(emitter));
|
||||
}
|
||||
for (var k = 0; k < propOuts.length; k++) {
|
||||
this[propOuts[k]] = new core_1.EventEmitter();
|
||||
this.checkLastValues.push(INITIAL_VALUE);
|
||||
}
|
||||
}
|
||||
UpgradeNg1ComponentAdapter.prototype.ngOnChanges = function(changes) {
|
||||
for (var name in changes) {
|
||||
if (changes.hasOwnProperty(name)) {
|
||||
var change = changes[name];
|
||||
this.setComponentProperty(name, change.currentValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.ngDoCheck = function() {
|
||||
var count = 0;
|
||||
var destinationObj = this.destinationObj;
|
||||
var lastValues = this.checkLastValues;
|
||||
var checkProperties = this.checkProperties;
|
||||
for (var i = 0; i < checkProperties.length; i++) {
|
||||
var value = destinationObj[checkProperties[i]];
|
||||
var last = lastValues[i];
|
||||
if (value !== last) {
|
||||
if (typeof value == 'number' && isNaN(value) && typeof last == 'number' && isNaN(last)) {} else {
|
||||
var eventEmitter = this[this.propOuts[i]];
|
||||
eventEmitter.emit(lastValues[i] = value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.setComponentProperty = function(name, value) {
|
||||
this.destinationObj[this.propertyMap[name]] = value;
|
||||
};
|
||||
UpgradeNg1ComponentAdapter.prototype.resolveRequired = function($element, require) {
|
||||
if (!require) {
|
||||
return undefined;
|
||||
} else if (typeof require == 'string') {
|
||||
var name = require;
|
||||
var isOptional = false;
|
||||
var startParent = false;
|
||||
var searchParents = false;
|
||||
var ch;
|
||||
if (name.charAt(0) == '?') {
|
||||
isOptional = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
searchParents = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
if (name.charAt(0) == '^') {
|
||||
startParent = true;
|
||||
name = name.substr(1);
|
||||
}
|
||||
var key = util_1.controllerKey(name);
|
||||
if (startParent)
|
||||
$element = $element.parent();
|
||||
var dep = searchParents ? $element.inheritedData(key) : $element.data(key);
|
||||
if (!dep && !isOptional) {
|
||||
throw new Error("Can not locate '" + require + "' in '" + this.directive.name + "'.");
|
||||
}
|
||||
return dep;
|
||||
} else if (require instanceof Array) {
|
||||
var deps = [];
|
||||
for (var i = 0; i < require.length; i++) {
|
||||
deps.push(this.resolveRequired($element, require[i]));
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
throw new Error("Directive '" + this.directive.name + "' require syntax unrecognized: " + this.directive.require);
|
||||
};
|
||||
return UpgradeNg1ComponentAdapter;
|
||||
})();
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/src/upgrade/upgrade_adapter", ["angular2/core", "angular2/src/facade/async", "angular2/platform/browser", "angular2/src/upgrade/metadata", "angular2/src/upgrade/util", "angular2/src/upgrade/constants", "angular2/src/upgrade/downgrade_ng2_adapter", "angular2/src/upgrade/upgrade_ng1_adapter", "angular2/src/upgrade/angular_js"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var core_1 = require("angular2/core");
|
||||
var async_1 = require("angular2/src/facade/async");
|
||||
var browser_1 = require("angular2/platform/browser");
|
||||
var metadata_1 = require("angular2/src/upgrade/metadata");
|
||||
var util_1 = require("angular2/src/upgrade/util");
|
||||
var constants_1 = require("angular2/src/upgrade/constants");
|
||||
var downgrade_ng2_adapter_1 = require("angular2/src/upgrade/downgrade_ng2_adapter");
|
||||
var upgrade_ng1_adapter_1 = require("angular2/src/upgrade/upgrade_ng1_adapter");
|
||||
var angular = require("angular2/src/upgrade/angular_js");
|
||||
var upgradeCount = 0;
|
||||
var UpgradeAdapter = (function() {
|
||||
function UpgradeAdapter() {
|
||||
this.idPrefix = "NG2_UPGRADE_" + upgradeCount++ + "_";
|
||||
this.upgradedComponents = [];
|
||||
this.downgradedComponents = {};
|
||||
this.providers = [];
|
||||
}
|
||||
UpgradeAdapter.prototype.downgradeNg2Component = function(type) {
|
||||
this.upgradedComponents.push(type);
|
||||
var info = metadata_1.getComponentInfo(type);
|
||||
return ng1ComponentDirective(info, "" + this.idPrefix + info.selector + "_c");
|
||||
};
|
||||
UpgradeAdapter.prototype.upgradeNg1Component = function(name) {
|
||||
if (this.downgradedComponents.hasOwnProperty(name)) {
|
||||
return this.downgradedComponents[name].type;
|
||||
} else {
|
||||
return (this.downgradedComponents[name] = new upgrade_ng1_adapter_1.UpgradeNg1ComponentAdapterBuilder(name)).type;
|
||||
}
|
||||
};
|
||||
UpgradeAdapter.prototype.bootstrap = function(element, modules, config) {
|
||||
var _this = this;
|
||||
var upgrade = new UpgradeAdapterRef();
|
||||
var ng1Injector = null;
|
||||
var platformRef = core_1.platform(browser_1.BROWSER_PROVIDERS);
|
||||
var applicationRef = platformRef.application([browser_1.BROWSER_APP_PROVIDERS, core_1.provide(constants_1.NG1_INJECTOR, {useFactory: function() {
|
||||
return ng1Injector;
|
||||
}}), core_1.provide(constants_1.NG1_COMPILE, {useFactory: function() {
|
||||
return ng1Injector.get(constants_1.NG1_COMPILE);
|
||||
}}), this.providers]);
|
||||
var injector = applicationRef.injector;
|
||||
var ngZone = injector.get(core_1.NgZone);
|
||||
var compiler = injector.get(core_1.Compiler);
|
||||
var delayApplyExps = [];
|
||||
var original$applyFn;
|
||||
var rootScopePrototype;
|
||||
var rootScope;
|
||||
var protoViewRefMap = {};
|
||||
var ng1Module = angular.module(this.idPrefix, modules);
|
||||
var ng1compilePromise = null;
|
||||
ng1Module.value(constants_1.NG2_INJECTOR, injector).value(constants_1.NG2_ZONE, ngZone).value(constants_1.NG2_COMPILER, compiler).value(constants_1.NG2_PROTO_VIEW_REF_MAP, protoViewRefMap).value(constants_1.NG2_APP_VIEW_MANAGER, injector.get(core_1.AppViewManager)).config(['$provide', function(provide) {
|
||||
provide.decorator(constants_1.NG1_ROOT_SCOPE, ['$delegate', function(rootScopeDelegate) {
|
||||
rootScopePrototype = rootScopeDelegate.constructor.prototype;
|
||||
if (rootScopePrototype.hasOwnProperty('$apply')) {
|
||||
original$applyFn = rootScopePrototype.$apply;
|
||||
rootScopePrototype.$apply = function(exp) {
|
||||
return delayApplyExps.push(exp);
|
||||
};
|
||||
} else {
|
||||
throw new Error("Failed to find '$apply' on '$rootScope'!");
|
||||
}
|
||||
return rootScope = rootScopeDelegate;
|
||||
}]);
|
||||
}]).run(['$injector', '$rootScope', function(injector, rootScope) {
|
||||
ng1Injector = injector;
|
||||
async_1.ObservableWrapper.subscribe(ngZone.onTurnDone, function(_) {
|
||||
ngZone.run(function() {
|
||||
return rootScope.$apply();
|
||||
});
|
||||
});
|
||||
ng1compilePromise = upgrade_ng1_adapter_1.UpgradeNg1ComponentAdapterBuilder.resolve(_this.downgradedComponents, injector);
|
||||
}]);
|
||||
angular.element(element).data(util_1.controllerKey(constants_1.NG2_INJECTOR), injector);
|
||||
ngZone.run(function() {
|
||||
angular.bootstrap(element, [_this.idPrefix], config);
|
||||
});
|
||||
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise]).then(function() {
|
||||
ngZone.run(function() {
|
||||
if (rootScopePrototype) {
|
||||
rootScopePrototype.$apply = original$applyFn;
|
||||
while (delayApplyExps.length) {
|
||||
rootScope.$apply(delayApplyExps.shift());
|
||||
}
|
||||
upgrade._bootstrapDone(applicationRef, ng1Injector);
|
||||
rootScopePrototype = null;
|
||||
}
|
||||
});
|
||||
}, util_1.onError);
|
||||
return upgrade;
|
||||
};
|
||||
UpgradeAdapter.prototype.addProvider = function(provider) {
|
||||
this.providers.push(provider);
|
||||
};
|
||||
UpgradeAdapter.prototype.upgradeNg1Provider = function(name, options) {
|
||||
var token = options && options.asToken || name;
|
||||
this.providers.push(core_1.provide(token, {
|
||||
useFactory: function(ng1Injector) {
|
||||
return ng1Injector.get(name);
|
||||
},
|
||||
deps: [constants_1.NG1_INJECTOR]
|
||||
}));
|
||||
};
|
||||
UpgradeAdapter.prototype.downgradeNg2Provider = function(token) {
|
||||
var factory = function(injector) {
|
||||
return injector.get(token);
|
||||
};
|
||||
factory.$inject = [constants_1.NG2_INJECTOR];
|
||||
return factory;
|
||||
};
|
||||
UpgradeAdapter.prototype.compileNg2Components = function(compiler, protoViewRefMap) {
|
||||
var _this = this;
|
||||
var promises = [];
|
||||
var types = this.upgradedComponents;
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
promises.push(compiler.compileInHost(types[i]));
|
||||
}
|
||||
return Promise.all(promises).then(function(protoViews) {
|
||||
var types = _this.upgradedComponents;
|
||||
for (var i = 0; i < protoViews.length; i++) {
|
||||
protoViewRefMap[metadata_1.getComponentInfo(types[i]).selector] = protoViews[i];
|
||||
}
|
||||
return protoViewRefMap;
|
||||
}, util_1.onError);
|
||||
};
|
||||
return UpgradeAdapter;
|
||||
})();
|
||||
exports.UpgradeAdapter = UpgradeAdapter;
|
||||
function ng1ComponentDirective(info, idPrefix) {
|
||||
directiveFactory.$inject = [constants_1.NG2_PROTO_VIEW_REF_MAP, constants_1.NG2_APP_VIEW_MANAGER, constants_1.NG1_PARSE];
|
||||
function directiveFactory(protoViewRefMap, viewManager, parse) {
|
||||
var protoView = protoViewRefMap[info.selector];
|
||||
if (!protoView)
|
||||
throw new Error('Expecting ProtoViewRef for: ' + info.selector);
|
||||
var idCount = 0;
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: constants_1.REQUIRE_INJECTOR,
|
||||
link: {post: function(scope, element, attrs, parentInjector, transclude) {
|
||||
var domElement = element[0];
|
||||
var facade = new downgrade_ng2_adapter_1.DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element, attrs, scope, parentInjector, parse, viewManager, protoView);
|
||||
facade.setupInputs();
|
||||
facade.bootstrapNg2();
|
||||
facade.projectContent();
|
||||
facade.setupOutputs();
|
||||
facade.registerCleanup();
|
||||
}}
|
||||
};
|
||||
}
|
||||
return directiveFactory;
|
||||
}
|
||||
var UpgradeAdapterRef = (function() {
|
||||
function UpgradeAdapterRef() {
|
||||
this._readyFn = null;
|
||||
this.ng1RootScope = null;
|
||||
this.ng1Injector = null;
|
||||
this.ng2ApplicationRef = null;
|
||||
this.ng2Injector = null;
|
||||
}
|
||||
UpgradeAdapterRef.prototype._bootstrapDone = function(applicationRef, ng1Injector) {
|
||||
this.ng2ApplicationRef = applicationRef;
|
||||
this.ng2Injector = applicationRef.injector;
|
||||
this.ng1Injector = ng1Injector;
|
||||
this.ng1RootScope = ng1Injector.get(constants_1.NG1_ROOT_SCOPE);
|
||||
this._readyFn && this._readyFn(this);
|
||||
};
|
||||
UpgradeAdapterRef.prototype.ready = function(fn) {
|
||||
this._readyFn = fn;
|
||||
};
|
||||
UpgradeAdapterRef.prototype.dispose = function() {
|
||||
this.ng1Injector.get(constants_1.NG1_ROOT_SCOPE).$destroy();
|
||||
this.ng2ApplicationRef.dispose();
|
||||
};
|
||||
return UpgradeAdapterRef;
|
||||
})();
|
||||
exports.UpgradeAdapterRef = UpgradeAdapterRef;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
System.register("angular2/upgrade", ["angular2/src/upgrade/upgrade_adapter"], true, function(require, exports, module) {
|
||||
var global = System.global,
|
||||
__define = global.define;
|
||||
global.define = undefined;
|
||||
var upgrade_adapter_1 = require("angular2/src/upgrade/upgrade_adapter");
|
||||
exports.UpgradeAdapter = upgrade_adapter_1.UpgradeAdapter;
|
||||
exports.UpgradeAdapterRef = upgrade_adapter_1.UpgradeAdapterRef;
|
||||
global.define = __define;
|
||||
return module.exports;
|
||||
});
|
||||
|
||||
//# sourceMappingURLDisabled=upgrade.js.map
|
File diff suppressed because one or more lines are too long
4
public/vendor/npm/angular2/common.d.ts
vendored
4
public/vendor/npm/angular2/common.d.ts
vendored
@ -1,4 +0,0 @@
|
||||
export * from './src/common/pipes';
|
||||
export * from './src/common/directives';
|
||||
export * from './src/common/forms';
|
||||
export * from './src/common/common_directives';
|
8
public/vendor/npm/angular2/compiler.d.ts
vendored
8
public/vendor/npm/angular2/compiler.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Starting point to import all compiler APIs.
|
||||
*/
|
||||
export * from './src/compiler/url_resolver';
|
||||
export * from './src/compiler/xhr';
|
||||
export * from './src/compiler/compiler';
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user