UNPKG

@spalger/kibana

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

290 lines (238 loc) 9.46 kB
define(function (require) { var _ = require('lodash'); require('plugins/kibana/visualize/saved_visualizations/saved_visualizations'); require('plugins/kibana/visualize/editor/sidebar'); require('plugins/kibana/visualize/editor/agg_filter'); require('ui/visualize'); require('ui/clipboard'); require('ui/routes') .when('/visualize/create', { template: require('plugins/kibana/visualize/editor/editor.html'), resolve: { savedVis: function (savedVisualizations, courier, $route, Private) { var visTypes = Private(require('ui/registry/vis_types')); var visType = _.find(visTypes, {name: $route.current.params.type}); if (visType.requiresSearch && !$route.current.params.indexPattern && !$route.current.params.savedSearchId) { throw new Error('You must provide either an indexPattern or a savedSearchId'); } return savedVisualizations.get($route.current.params) .catch(courier.redirectWhenMissing({ '*': '/visualize' })); } } }) .when('/visualize/edit/:id', { template: require('plugins/kibana/visualize/editor/editor.html'), resolve: { savedVis: function (savedVisualizations, courier, $route) { return savedVisualizations.get($route.current.params.id) .catch(courier.redirectWhenMissing({ 'visualization': '/visualize', 'search': '/settings/objects/savedVisualizations/' + $route.current.params.id, 'index-pattern': '/settings/objects/savedVisualizations/' + $route.current.params.id, 'index-pattern-field': '/settings/objects/savedVisualizations/' + $route.current.params.id })); } } }); require('ui/modules') .get('app/visualize', [ 'kibana/notify', 'kibana/courier' ]) .controller('VisEditor', function ($scope, $route, timefilter, AppState, $location, kbnUrl, $timeout, courier, Private, Promise) { var angular = require('angular'); var ConfigTemplate = require('ui/ConfigTemplate'); var Notifier = require('ui/notify/Notifier'); var docTitle = Private(require('ui/doc_title')); var brushEvent = Private(require('ui/utils/brush_event')); var queryFilter = Private(require('ui/filter_bar/query_filter')); var filterBarClickHandler = Private(require('ui/filter_bar/filter_bar_click_handler')); var notify = new Notifier({ location: 'Visualization Editor' }); var savedVis = $route.current.locals.savedVis; var vis = savedVis.vis; var editableVis = vis.createEditableVis(); vis.requesting = function () { var requesting = editableVis.requesting; requesting.call(vis); requesting.call(editableVis); }; var searchSource = savedVis.searchSource; // config panel templates var configTemplate = new ConfigTemplate({ save: require('plugins/kibana/visualize/editor/panels/save.html'), load: require('plugins/kibana/visualize/editor/panels/load.html'), share: require('plugins/kibana/visualize/editor/panels/share.html'), }); if (savedVis.id) { docTitle.change(savedVis.title); } var $state = $scope.$state = (function initState() { var savedVisState = vis.getState(); var stateDefaults = { linked: !!savedVis.savedSearchId, query: searchSource.getOwn('query') || {query_string: {query: '*'}}, filters: searchSource.getOwn('filter') || [], vis: savedVisState }; $state = new AppState(stateDefaults); if (!angular.equals($state.vis, savedVisState)) { Promise.try(function () { vis.setState($state.vis); editableVis.setState($state.vis); }) .catch(courier.redirectWhenMissing({ 'index-pattern-field': '/visualize' })); } return $state; }()); function init() { // export some objects $scope.savedVis = savedVis; $scope.searchSource = searchSource; $scope.vis = vis; $scope.indexPattern = vis.indexPattern; $scope.editableVis = editableVis; $scope.state = $state; $scope.conf = _.pick($scope, 'doSave', 'savedVis', 'shareData'); $scope.configTemplate = configTemplate; editableVis.listeners.click = vis.listeners.click = filterBarClickHandler($state); editableVis.listeners.brush = vis.listeners.brush = brushEvent; // track state of editable vis vs. "actual" vis $scope.stageEditableVis = transferVisState(editableVis, vis, true); $scope.resetEditableVis = transferVisState(vis, editableVis); $scope.$watch(function () { return editableVis.getState(); }, function (newState) { editableVis.dirty = !angular.equals(newState, vis.getState()); $scope.responseValueAggs = null; try { $scope.responseValueAggs = editableVis.aggs.getResponseAggs().filter(function (agg) { return _.get(agg, 'schema.group') === 'metrics'; }); } catch (e) { // this can fail when the agg.type is changed but the // params have not been set yet. watcher will trigger again // when the params update } }, true); $state.replace(); $scope.$watch('searchSource.get("index").timeFieldName', function (timeField) { timefilter.enabled = !!timeField; }); // update the searchSource when filters update $scope.$listen(queryFilter, 'update', function () { searchSource.set('filter', queryFilter.getFilters()); $state.save(); }); // fetch data when filters fire fetch event $scope.$listen(queryFilter, 'fetch', $scope.fetch); $scope.$listen($state, 'fetch_with_changes', function (keys) { if (_.contains(keys, 'linked') && $state.linked === true) { // abort and reload route $route.reload(); return; } if (_.contains(keys, 'vis')) { $state.vis.listeners = _.defaults($state.vis.listeners || {}, vis.listeners); // only update when we need to, otherwise colors change and we // risk loosing an in-progress result vis.setState($state.vis); editableVis.setState($state.vis); } // we use state to track query, must write before we fetch if ($state.query && !$state.linked) { searchSource.set('query', $state.query); } else { searchSource.set('query', null); } if (_.isEqual(keys, ['filters'])) { // updates will happen in filter watcher if needed return; } $scope.fetch(); }); // Without this manual emission, we'd miss filters and queries that were on the $state initially $state.emit('fetch_with_changes'); $scope.$listen(timefilter, 'fetch', _.bindKey($scope, 'fetch')); $scope.$on('ready:vis', function () { $scope.$emit('application.load'); }); $scope.$on('$destroy', function () { savedVis.destroy(); }); } $scope.fetch = function () { $state.save(); searchSource.set('filter', queryFilter.getFilters()); if (!$state.linked) searchSource.set('query', $state.query); if ($scope.vis.type.requiresSearch) { courier.fetch(); } }; $scope.startOver = function () { kbnUrl.change('/visualize', {}); }; $scope.doSave = function () { savedVis.id = savedVis.title; savedVis.visState = $state.vis; savedVis.save() .then(function (id) { configTemplate.close('save'); if (id) { notify.info('Saved Visualization "' + savedVis.title + '"'); if (savedVis.id === $route.current.params.id) return; kbnUrl.change('/visualize/edit/{{id}}', {id: savedVis.id}); } }, notify.fatal); }; $scope.shareData = function () { return { link: $location.absUrl(), // This sucks, but seems like the cleanest way. Uhg. embed: '<iframe src="' + $location.absUrl().replace('?', '?embed&') + '" height="600" width="800"></iframe>' }; }; $scope.unlink = function () { if (!$state.linked) return; $state.linked = false; var parent = searchSource.getParent(true); var parentsParent = parent.getParent(true); // display unlinking for 2 seconds, unless it is double clicked $scope.unlinking = $timeout($scope.clearUnlinking, 2000); delete savedVis.savedSearchId; parent.set('filter', _.union(searchSource.getOwn('filter'), parent.getOwn('filter'))); // copy over all state except "aggs" and filter, which is already copied _(parent.toJSON()) .omit('aggs') .forOwn(function (val, key) { searchSource.set(key, val); }) .commit(); $state.query = searchSource.get('query'); $state.filters = searchSource.get('filter'); searchSource.inherits(parentsParent); }; $scope.clearUnlinking = function () { if ($scope.unlinking) { $timeout.cancel($scope.unlinking); $scope.unlinking = null; } }; function transferVisState(fromVis, toVis, fetch) { return function () { toVis.setState(fromVis.getState()); editableVis.dirty = false; $state.vis = vis.getState(); $state.save(); if (fetch) $scope.fetch(); }; } init(); }); });