UNPKG

kibana-123

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

250 lines (210 loc) 8.12 kB
require('./flot'); require('plugins/timelion/panels/timechart/timechart.less'); var _ = require('lodash'); var $ = require('jquery'); var moment = require('moment-timezone'); var observeResize = require('plugins/timelion/lib/observe_resize'); var calculateInterval = require('plugins/timelion/lib/calculate_interval'); module.exports = function timechartFn(Private, config, $rootScope, timefilter, $compile) { return function () { return { help: 'Draw a timeseries chart', render: function ($scope, $elem) { var template = '<div class="chart-top-title"></div><div class="chart-canvas"></div>'; var timezone = Private(require('plugins/timelion/services/timezone'))(); var getxAxisFormatter = Private(require('plugins/timelion/panels/timechart/xaxis_formatter')); // TODO: I wonder if we should supply our own moment that sets this every time? // could just use angular's injection to provide a moment service? moment.tz.setDefault(config.get('dateFormat:tz')); var render = $scope.seriesList.render || {}; $scope.chart = $scope.seriesList.list; $scope.interval = $scope.interval; $scope.search = $scope.search || _.noop; var legendValueNumbers; var debouncedSetLegendNumbers; var defaultOptions = { xaxis: { mode: 'time', tickLength: 5, timezone: 'browser', }, selection: { mode: 'x', color: '#ccc' }, crosshair: { mode: 'x', color: '#C66', lineWidth: 2 }, grid: { show: render.grid, borderWidth: 0, borderColor: null, margin: 10, hoverable: true, autoHighlight: false }, legend: { backgroundColor: null, position: 'nw', labelBoxBorderColor: 'rgb(255,255,255,0)', labelFormatter: function (label, series) { return '<span class="ngLegendValue" ng-click="toggleSeries(' + series._id + ')">' + label + '<span class="ngLegendValueNumber"></span></span>'; } }, colors: ['#01A4A4', '#C66', '#D0D102', '#616161', '#00A1CB', '#32742C', '#F18D05', '#113F8C', '#61AE24', '#D70060'] }; $scope.toggleSeries = function (id) { var series = $scope.chart[id]; series._hide = !series._hide; drawPlot($scope.chart); }; var cancelResize = observeResize($elem, function () { drawPlot($scope.chart); }); $scope.$on('$destroy', function () { cancelResize(); $elem.off('plothover'); $elem.off('plotselected'); $elem.off('mouseleave'); }); $elem.on('plothover', function (event, pos, item) { $rootScope.$broadcast('timelionPlotHover', event, pos, item); }); $elem.on('plotselected', function (event, ranges) { timefilter.time.from = moment(ranges.xaxis.from); timefilter.time.to = moment(ranges.xaxis.to); timefilter.time.mode = 'absolute'; $scope.search(); }); $elem.on('mouseleave', function () { $rootScope.$broadcast('timelionPlotLeave'); }); $scope.$on('timelionPlotHover', function (angularEvent, flotEvent, pos, time) { if (!$scope.plot) return; $scope.plot.setCrosshair(pos); debouncedSetLegendNumbers(pos); }); $scope.$on('timelionPlotLeave', function (angularEvent, flotEvent, pos, time) { if (!$scope.plot) return; $scope.plot.clearCrosshair(); clearLegendNumbers(); }); var debounceDelay = 50; debouncedSetLegendNumbers = _.debounce(setLegendNumbers, debounceDelay, { maxWait: debounceDelay, leading: true, trailing: false }); // Shamelessly borrowed from the flotCrosshairs example function setLegendNumbers(pos) { var plot = $scope.plot; var axes = plot.getAxes(); if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max) { return; } var i; var j; var dataset = plot.getData(); for (i = 0; i < dataset.length; ++i) { var series = dataset[i]; var precision = _.get(series, '_meta.precision', 2); if (series._hide) continue; // Nearest point for (j = 0; j < series.data.length; ++j) { if (series.data[j][0] > pos.x) break; } var y; try { y = series.data[j][1]; } catch (e) { y = null; } if (y != null) { legendValueNumbers.eq(i).text('(' + y.toFixed(precision) + ')'); } else { legendValueNumbers.eq(i).empty(); } } } function clearLegendNumbers() { _.each(legendValueNumbers, function (num) { $(num).empty(); }); } var legendScope = $scope.$new(); function drawPlot(plotConfig) { if (!plotConfig || !plotConfig.length) { $elem.empty(); return; } if (!$('.chart-canvas', $elem).length) $elem.html(template); var canvasElem = $('.chart-canvas', $elem); var title = _(plotConfig).map('_title').compact().last(); $('.chart-top-title', $elem).text(title == null ? '' : title); var options = _.cloneDeep(defaultOptions); // Get the X-axis tick format var time = timefilter.getBounds(); var interval = calculateInterval( time.min.valueOf(), time.max.valueOf(), config.get('timelion:target_buckets') || 200, $scope.interval ); var format = getxAxisFormatter(interval); // Use moment to format ticks so we get timezone correction options.xaxis.tickFormatter = function (val) { return moment(val).format(format); }; // Calculate how many ticks can fit on the axis var tickLetterWidth = 7; var tickPadding = 45; options.xaxis.ticks = Math.floor($elem.width() / ((format.length * tickLetterWidth) + tickPadding)); var series = _.map(plotConfig, function (series, index) { series = _.cloneDeep(_.defaults(series, { shadowSize: 0, lines: { lineWidth: 3 } })); series._id = index; if (series._hide) { series.data = []; series.stack = false; //series.color = "#ddd"; series.label = '(hidden) ' + series.label; } if (series._global) { _.merge(options, series._global, function (objVal, srcVal) { // This is kind of gross, it means that you can't replace a global value with a null // best you can do is an empty string. Deal with it. if (objVal == null) return srcVal; if (srcVal == null) return objVal; }); } return series; }); try { $scope.plot = $.plot(canvasElem, _.compact(series), options); } catch (e) { setTimeout(drawPlot, 500); } if ($scope.plot) { $scope.$emit('renderComplete'); } legendScope.$destroy(); legendScope = $scope.$new(); // Used to toggle the series, and for displaying values on hover legendValueNumbers = canvasElem.find('.ngLegendValueNumber'); _.each(canvasElem.find('.ngLegendValue'), function (elem) { $compile(elem)(legendScope); }); } $scope.$watch('chart', drawPlot); } }; }; };