Merge branch 'master' into preventHomeDirAccess
This commit is contained in:
commit
c2cb8761a1
51 changed files with 1136 additions and 147 deletions
|
@ -505,6 +505,13 @@ d3.selectAll('g.bar')
|
|||
.each(function() ... )
|
||||
```
|
||||
|
||||
```js
|
||||
$http.get('/info')
|
||||
.then(({ data }) => this.transfromInfo(data))
|
||||
.then((transformed) => $http.post('/new-info', transformed))
|
||||
.then(({ data }) => console.log(data));
|
||||
```
|
||||
|
||||
*Wrong:*
|
||||
|
||||
```js
|
||||
|
@ -519,6 +526,13 @@ d3.selectAll('g.bar')
|
|||
.each(function() ... )
|
||||
```
|
||||
|
||||
```js
|
||||
$http.get('/info')
|
||||
.then(({ data }) => this.transfromInfo(data))
|
||||
.then((transformed) => $http.post('/new-info', transformed))
|
||||
.then(({ data }) => console.log(data));
|
||||
```
|
||||
|
||||
## Name your closures
|
||||
|
||||
Feel free to give your closures a descriptive name. It shows that you care about them, and
|
||||
|
@ -867,7 +881,7 @@ When a node has multiple attributes that would cause it to exceed the line chara
|
|||
attribute1="value1"
|
||||
attribute2="value2"
|
||||
attribute3="value3">
|
||||
|
||||
|
||||
<li></li>
|
||||
<li></li>
|
||||
...
|
||||
|
|
|
@ -34,8 +34,8 @@ module.exports = function (program) {
|
|||
.option('-e, --elasticsearch <uri>', 'Elasticsearch instance')
|
||||
.option(
|
||||
'-c, --config <path>',
|
||||
'Path to the config file',
|
||||
fromRoot('config/kibana.yml'))
|
||||
'Path to the config file, can be changed with the CONFIG_PATH environment variable as well',
|
||||
process.env.CONFIG_PATH || fromRoot('config/kibana.yml'))
|
||||
.option('-p, --port <port>', 'The port to bind to', parseInt)
|
||||
.option('-q, --quiet', 'Prevent all logging except errors')
|
||||
.option('-Q, --silent', 'Prevent all logging')
|
||||
|
|
|
@ -19,7 +19,7 @@ module.exports = function (kibana) {
|
|||
startupTimeout: Joi.number().default(5000),
|
||||
ssl: Joi.object({
|
||||
verify: Joi.boolean().default(true),
|
||||
ca: Joi.string(),
|
||||
ca: Joi.array().single().items(Joi.string()),
|
||||
cert: Joi.string(),
|
||||
key: Joi.string()
|
||||
}).default(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var url = require('url');
|
||||
var _ = require('lodash');
|
||||
var readFile = _.partialRight(require('fs').readFileSync, 'utf8');
|
||||
var readFile = (file) => require('fs').readFileSync(file, 'utf8');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
|
||||
|
@ -14,8 +14,8 @@ module.exports = _.memoize(function (server) {
|
|||
rejectUnauthorized: config.get('elasticsearch.ssl.verify')
|
||||
};
|
||||
|
||||
if (config.get('elasticsearch.ssl.ca')) {
|
||||
agentOptions.ca = [readFile(config.get('elasticsearch.ssl.ca'))];
|
||||
if (_.size(config.get('elasticsearch.ssl.ca'))) {
|
||||
agentOptions.ca = config.get('elasticsearch.ssl.ca').map(readFile);
|
||||
}
|
||||
|
||||
// Add client certificate and key if required by elasticsearch
|
||||
|
@ -29,4 +29,4 @@ module.exports = _.memoize(function (server) {
|
|||
|
||||
// See https://lodash.com/docs#memoize: We use a Map() instead of the default, because we want the keys in the cache
|
||||
// to be the server objects, and by default these would be coerced to strings as keys (which wouldn't be useful)
|
||||
module.exports.cache = new Map();
|
||||
module.exports.cache = new Map();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var elasticsearch = require('elasticsearch');
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
var readFile = (file) => require('fs').readFileSync(file, 'utf8');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
var callWithRequest = require('./call_with_request');
|
||||
|
@ -31,11 +31,11 @@ module.exports = function (server) {
|
|||
|
||||
var ssl = { rejectUnauthorized: options.verifySsl };
|
||||
if (options.clientCrt && options.clientKey) {
|
||||
ssl.cert = fs.readFileSync(options.clientCrt, 'utf8');
|
||||
ssl.key = fs.readFileSync(options.clientKey, 'utf8');
|
||||
ssl.cert = readFile(options.clientCrt);
|
||||
ssl.key = readFile(options.clientKey);
|
||||
}
|
||||
if (options.ca) {
|
||||
ssl.ca = fs.readFileSync(options.ca, 'utf8');
|
||||
ssl.ca = options.ca.map(readFile);
|
||||
}
|
||||
|
||||
return new elasticsearch.Client({
|
||||
|
|
|
@ -17,6 +17,7 @@ define(function (require) {
|
|||
return {
|
||||
savedObj: savedSearch,
|
||||
panel: panel,
|
||||
uiState: savedSearch.uiStateJSON ? JSON.parse(savedSearch.uiStateJSON) : {},
|
||||
editUrl: savedSearches.urlFor(panel.id)
|
||||
};
|
||||
});
|
||||
|
|
|
@ -5,17 +5,18 @@ define(function (require) {
|
|||
|
||||
return function (panel, $scope) { // Function parameters here
|
||||
return savedVisualizations.get(panel.id)
|
||||
.then(function (savedVis) {
|
||||
// $scope.state comes via $scope inheritence from the dashboard app. Don't love this.
|
||||
savedVis.vis.listeners.click = filterBarClickHandler($scope.state);
|
||||
savedVis.vis.listeners.brush = brushEvent;
|
||||
.then(function (savedVis) {
|
||||
// $scope.state comes via $scope inheritence from the dashboard app. Don't love this.
|
||||
savedVis.vis.listeners.click = filterBarClickHandler($scope.state);
|
||||
savedVis.vis.listeners.brush = brushEvent;
|
||||
|
||||
return {
|
||||
savedObj: savedVis,
|
||||
panel: panel,
|
||||
editUrl: savedVisualizations.urlFor(panel.id)
|
||||
};
|
||||
});
|
||||
return {
|
||||
savedObj: savedVis,
|
||||
panel: panel,
|
||||
uiState: savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : {},
|
||||
editUrl: savedVisualizations.urlFor(panel.id)
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
<visualize ng-switch-when="visualization"
|
||||
vis="savedObj.vis"
|
||||
search-source="savedObj.searchSource"
|
||||
show-spy-panel="chrome.getVisible()"
|
||||
ui-state="uiState"
|
||||
class="panel-content">
|
||||
</visualize>
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ define(function (require) {
|
|||
|
||||
var brushEvent = Private(require('ui/utils/brush_event'));
|
||||
|
||||
var getPanelId = function (panel) {
|
||||
return ['P', panel.panelIndex].join('-');
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('plugins/kibana/dashboard/components/panel/panel.html'),
|
||||
|
@ -39,7 +43,14 @@ define(function (require) {
|
|||
// These could be done in loadPanel, putting them here to make them more explicit
|
||||
$scope.savedObj = panelConfig.savedObj;
|
||||
$scope.editUrl = panelConfig.editUrl;
|
||||
$scope.$on('$destroy', panelConfig.savedObj.destroy);
|
||||
$scope.$on('$destroy', function () {
|
||||
panelConfig.savedObj.destroy();
|
||||
$scope.parentUiState.removeChild(getPanelId(panelConfig.panel));
|
||||
});
|
||||
|
||||
// create child ui state from the savedObj
|
||||
var uiState = panelConfig.uiState || {};
|
||||
$scope.uiState = $scope.parentUiState.createChild(getPanelId(panelConfig.panel), uiState, true);
|
||||
|
||||
$scope.filter = function (field, value, operator) {
|
||||
var index = $scope.savedObj.searchSource.get('index').id;
|
||||
|
|
|
@ -157,6 +157,7 @@ define(function (require) {
|
|||
|
||||
panel.$scope = $scope.$new();
|
||||
panel.$scope.panel = panel;
|
||||
panel.$scope.parentUiState = $scope.uiState;
|
||||
|
||||
panel.$el = $compile('<li><dashboard-panel></li>')(panel.$scope);
|
||||
|
||||
|
|
|
@ -81,11 +81,14 @@ define(function (require) {
|
|||
title: dash.title,
|
||||
panels: dash.panelsJSON ? JSON.parse(dash.panelsJSON) : [],
|
||||
options: dash.optionsJSON ? JSON.parse(dash.optionsJSON) : {},
|
||||
uiState: dash.uiStateJSON ? JSON.parse(dash.uiStateJSON) : {},
|
||||
query: extractQueryFromFilters(dash.searchSource.getOwn('filter')) || {query_string: {query: '*'}},
|
||||
filters: _.reject(dash.searchSource.getOwn('filter'), matchQueryFilter),
|
||||
};
|
||||
|
||||
var $state = $scope.state = new AppState(stateDefaults);
|
||||
var $uiState = $scope.uiState = $state.makeStateful('uiState');
|
||||
|
||||
$scope.$watchCollection('state.options', function (newVal, oldVal) {
|
||||
if (!angular.equals(newVal, oldVal)) $state.save();
|
||||
});
|
||||
|
@ -115,9 +118,30 @@ define(function (require) {
|
|||
docTitle.change(dash.title);
|
||||
}
|
||||
|
||||
initPanelIndices();
|
||||
$scope.$emit('application.load');
|
||||
}
|
||||
|
||||
function initPanelIndices() {
|
||||
// find the largest panelIndex in all the panels
|
||||
var maxIndex = getMaxPanelIndex();
|
||||
|
||||
// ensure that all panels have a panelIndex
|
||||
$scope.state.panels.forEach(function (panel) {
|
||||
if (!panel.panelIndex) {
|
||||
panel.panelIndex = maxIndex++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMaxPanelIndex() {
|
||||
var index = $scope.state.panels.reduce(function (idx, panel) {
|
||||
// if panel is missing an index, add one and increment the index
|
||||
return Math.max(idx, panel.panelIndex || idx);
|
||||
}, 0);
|
||||
return ++index;
|
||||
}
|
||||
|
||||
function updateQueryOnRootSource() {
|
||||
var filters = queryFilter.getFilters();
|
||||
if ($state.query) {
|
||||
|
@ -157,7 +181,9 @@ define(function (require) {
|
|||
$scope.save = function () {
|
||||
$state.title = dash.id = dash.title;
|
||||
$state.save();
|
||||
|
||||
dash.panelsJSON = angular.toJson($state.panels);
|
||||
dash.uiStateJSON = angular.toJson($uiState.getChanges());
|
||||
dash.timeFrom = dash.timeRestore ? timefilter.time.from : undefined;
|
||||
dash.timeTo = dash.timeRestore ? timefilter.time.to : undefined;
|
||||
dash.optionsJSON = angular.toJson($state.options);
|
||||
|
@ -193,12 +219,12 @@ define(function (require) {
|
|||
// called by the saved-object-finder when a user clicks a vis
|
||||
$scope.addVis = function (hit) {
|
||||
pendingVis++;
|
||||
$state.panels.push({ id: hit.id, type: 'visualization' });
|
||||
$state.panels.push({ id: hit.id, type: 'visualization', panelIndex: getMaxPanelIndex() });
|
||||
};
|
||||
|
||||
$scope.addSearch = function (hit) {
|
||||
pendingVis++;
|
||||
$state.panels.push({ id: hit.id, type: 'search' });
|
||||
$state.panels.push({ id: hit.id, type: 'search', panelIndex: getMaxPanelIndex() });
|
||||
};
|
||||
|
||||
// Setup configurable values for config directive, after objects are initialized
|
||||
|
|
|
@ -11,7 +11,7 @@ define(function (require) {
|
|||
_.class(SavedDashboard).inherits(courier.SavedObject);
|
||||
function SavedDashboard(id) {
|
||||
// Gives our SavedDashboard the properties of a SavedObject
|
||||
courier.SavedObject.call(this, {
|
||||
SavedDashboard.Super.call(this, {
|
||||
type: SavedDashboard.type,
|
||||
mapping: SavedDashboard.mapping,
|
||||
searchSource: SavedDashboard.searchsource,
|
||||
|
@ -28,6 +28,7 @@ define(function (require) {
|
|||
optionsJSON: angular.toJson({
|
||||
darkTheme: config.get('dashboard:defaultDarkTheme')
|
||||
}),
|
||||
uiStateJSON: '{}',
|
||||
version: 1,
|
||||
timeRestore: false,
|
||||
timeTo: undefined,
|
||||
|
@ -50,6 +51,7 @@ define(function (require) {
|
|||
description: 'string',
|
||||
panelsJSON: 'string',
|
||||
optionsJSON: 'string',
|
||||
uiStateJSON: 'string',
|
||||
version: 'integer',
|
||||
timeRestore: 'boolean',
|
||||
timeTo: 'string',
|
||||
|
|
|
@ -26,7 +26,6 @@ define(function (require) {
|
|||
|
||||
// Returns a single dashboard by ID, should be the name of the dashboard
|
||||
this.get = function (id) {
|
||||
|
||||
// Returns a promise that contains a dashboard which is a subclass of docSource
|
||||
return (new SavedDashboard(id)).init();
|
||||
};
|
||||
|
@ -42,7 +41,6 @@ define(function (require) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
this.find = function (searchString, size = 100) {
|
||||
var self = this;
|
||||
var body;
|
||||
|
|
|
@ -10,7 +10,7 @@ var chrome = require('ui/chrome');
|
|||
var routes = require('ui/routes');
|
||||
var modules = require('ui/modules');
|
||||
|
||||
var kibanaLogoUrl = require('ui/images/kibana.png');
|
||||
var kibanaLogoUrl = require('ui/images/kibana.svg');
|
||||
|
||||
routes
|
||||
.otherwise({
|
||||
|
|
|
@ -126,8 +126,8 @@
|
|||
|
||||
<div class="vis-editor-content">
|
||||
|
||||
<div class="collapsible-sidebar">
|
||||
<vis-editor-sidebar class="vis-editor-sidebar" ng-if="chrome.getVisible()"></vis-editor-sidebar>
|
||||
<div class="collapsible-sidebar" ng-if="chrome.getVisible()">
|
||||
<vis-editor-sidebar class="vis-editor-sidebar"></vis-editor-sidebar>
|
||||
</div>
|
||||
|
||||
<div class="vis-editor-canvas" ng-class="{ embedded: !chrome.getVisible() }">
|
||||
|
@ -138,7 +138,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<visualize vis="vis" editable-vis="editableVis" search-source="savedVis.searchSource"></visualize>
|
||||
<visualize vis="vis" ui-state="uiState" show-spy-panel="chrome.getVisible()" search-source="savedVis.searchSource" editable-vis="editableVis"></visualize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ define(function (require) {
|
|||
var $state = $scope.$state = (function initState() {
|
||||
var savedVisState = vis.getState();
|
||||
var stateDefaults = {
|
||||
uiState: savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : {},
|
||||
linked: !!savedVis.savedSearchId,
|
||||
query: searchSource.getOwn('query') || {query_string: {query: '*'}},
|
||||
filters: searchSource.getOwn('filter') || [],
|
||||
|
@ -114,6 +115,8 @@ define(function (require) {
|
|||
$scope.indexPattern = vis.indexPattern;
|
||||
$scope.editableVis = editableVis;
|
||||
$scope.state = $state;
|
||||
$scope.uiState = $state.makeStateful('uiState');
|
||||
|
||||
$scope.conf = _.pick($scope, 'doSave', 'savedVis', 'shareData');
|
||||
$scope.configTemplate = configTemplate;
|
||||
|
||||
|
@ -210,7 +213,6 @@ define(function (require) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.startOver = function () {
|
||||
kbnUrl.change('/visualize', {});
|
||||
};
|
||||
|
@ -218,6 +220,7 @@ define(function (require) {
|
|||
$scope.doSave = function () {
|
||||
savedVis.id = savedVis.title;
|
||||
savedVis.visState = $state.vis;
|
||||
savedVis.uiStateJSON = angular.toJson($scope.uiState.getChanges());
|
||||
|
||||
savedVis.save()
|
||||
.then(function (id) {
|
||||
|
|
|
@ -30,6 +30,7 @@ define(function (require) {
|
|||
def.type = opts.type;
|
||||
return def;
|
||||
}()),
|
||||
uiStateJSON: '{}',
|
||||
description: '',
|
||||
savedSearchId: opts.savedSearchId,
|
||||
version: 1
|
||||
|
@ -44,6 +45,7 @@ define(function (require) {
|
|||
SavedVis.mapping = {
|
||||
title: 'string',
|
||||
visState: 'json',
|
||||
uiStateJSON: 'string',
|
||||
description: 'string',
|
||||
savedSearchId: 'string',
|
||||
version: 'integer'
|
||||
|
|
|
@ -20,9 +20,11 @@ define(function (require) {
|
|||
if (req && req.ms != null) stats.push(['Request Duration', req.ms + 'ms']);
|
||||
if (resp && resp.hits) stats.push(['Hits', resp.hits.total]);
|
||||
|
||||
if (req.fetchParams.index) stats.push(['Index', req.fetchParams.index]);
|
||||
if (req.fetchParams.type) stats.push(['Type', req.fetchParams.type]);
|
||||
if (req.fetchParams.id) stats.push(['Id', req.fetchParams.id]);
|
||||
if (req.fetchParams) {
|
||||
if (req.fetchParams.index) stats.push(['Index', req.fetchParams.index]);
|
||||
if (req.fetchParams.type) stats.push(['Type', req.fetchParams.type]);
|
||||
if (req.fetchParams.id) stats.push(['Id', req.fetchParams.id]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ let _ = require('lodash');
|
|||
let { zipObject } = require('lodash');
|
||||
let override = require('./override');
|
||||
let pkg = require('requirefrom')('src/utils')('packageJson');
|
||||
const clone = require('./deepCloneWithBuffers');
|
||||
|
||||
const schema = Symbol('Joi Schema');
|
||||
const schemaKeys = Symbol('Schema Extensions');
|
||||
|
@ -15,7 +16,7 @@ module.exports = class Config {
|
|||
this[schemaKeys] = new Map();
|
||||
|
||||
this[vals] = Object.create(null);
|
||||
this[pendingSets] = new Map(_.pairs(_.cloneDeep(initialSettings || {})));
|
||||
this[pendingSets] = new Map(_.pairs(clone(initialSettings || {})));
|
||||
|
||||
if (initialSchema) this.extendSchema(initialSchema);
|
||||
}
|
||||
|
@ -64,7 +65,7 @@ module.exports = class Config {
|
|||
|
||||
set(key, value) {
|
||||
// clone and modify the config
|
||||
let config = _.cloneDeep(this[vals]);
|
||||
let config = clone(this[vals]);
|
||||
if (_.isPlainObject(key)) {
|
||||
config = override(config, key);
|
||||
} else {
|
||||
|
@ -114,7 +115,7 @@ module.exports = class Config {
|
|||
|
||||
get(key) {
|
||||
if (!key) {
|
||||
return _.cloneDeep(this[vals]);
|
||||
return clone(this[vals]);
|
||||
}
|
||||
|
||||
let value = _.get(this[vals], key);
|
||||
|
@ -123,7 +124,7 @@ module.exports = class Config {
|
|||
throw new Error('Unknown config key: ' + key);
|
||||
}
|
||||
}
|
||||
return _.cloneDeep(value);
|
||||
return clone(value);
|
||||
}
|
||||
|
||||
has(key) {
|
||||
|
|
61
src/server/config/__tests__/deepCloneWithBuffers.js
Normal file
61
src/server/config/__tests__/deepCloneWithBuffers.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import deepCloneWithBuffers from '../deepCloneWithBuffers';
|
||||
import expect from 'expect.js';
|
||||
|
||||
describe('deepCloneWithBuffers()', function () {
|
||||
it('deep clones objects', function () {
|
||||
const source = {
|
||||
a: {
|
||||
b: {},
|
||||
c: {},
|
||||
d: [
|
||||
{
|
||||
e: 'f'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const output = deepCloneWithBuffers(source);
|
||||
|
||||
expect(source.a).to.eql(output.a);
|
||||
expect(source.a).to.not.be(output.a);
|
||||
|
||||
expect(source.a.b).to.eql(output.a.b);
|
||||
expect(source.a.b).to.not.be(output.a.b);
|
||||
|
||||
expect(source.a.c).to.eql(output.a.c);
|
||||
expect(source.a.c).to.not.be(output.a.c);
|
||||
|
||||
expect(source.a.d).to.eql(output.a.d);
|
||||
expect(source.a.d).to.not.be(output.a.d);
|
||||
|
||||
expect(source.a.d[0]).to.eql(output.a.d[0]);
|
||||
expect(source.a.d[0]).to.not.be(output.a.d[0]);
|
||||
});
|
||||
|
||||
it('copies buffers but keeps them buffers', function () {
|
||||
const input = new Buffer('i am a teapot', 'utf8');
|
||||
const output = deepCloneWithBuffers(input);
|
||||
|
||||
expect(Buffer.isBuffer(input)).to.be.ok();
|
||||
expect(Buffer.isBuffer(output)).to.be.ok();
|
||||
expect(Buffer.compare(output, input));
|
||||
expect(output).to.not.be(input);
|
||||
});
|
||||
|
||||
it('copies buffers that are deep', function () {
|
||||
const input = {
|
||||
a: {
|
||||
b: {
|
||||
c: new Buffer('i am a teapot', 'utf8')
|
||||
}
|
||||
}
|
||||
};
|
||||
const output = deepCloneWithBuffers(input);
|
||||
|
||||
expect(Buffer.isBuffer(input.a.b.c)).to.be.ok();
|
||||
expect(Buffer.isBuffer(output.a.b.c)).to.be.ok();
|
||||
expect(Buffer.compare(output.a.b.c, input.a.b.c));
|
||||
expect(output.a.b.c).to.not.be(input.a.b.c);
|
||||
});
|
||||
});
|
11
src/server/config/deepCloneWithBuffers.js
Normal file
11
src/server/config/deepCloneWithBuffers.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { cloneDeep } from 'lodash';
|
||||
|
||||
function cloneBuffersCustomizer(val) {
|
||||
if (Buffer.isBuffer(val)) {
|
||||
return new Buffer(val);
|
||||
}
|
||||
}
|
||||
|
||||
export default function (vals) {
|
||||
return cloneDeep(vals, cloneBuffersCustomizer);
|
||||
};
|
|
@ -113,6 +113,10 @@ define(function (require) {
|
|||
}),
|
||||
description: 'Maps values to specified colors within visualizations'
|
||||
},
|
||||
'visualization:loadingDelay': {
|
||||
value: '2s',
|
||||
description: 'Time to wait before dimming visualizations during query'
|
||||
},
|
||||
'csv:separator': {
|
||||
value: ',',
|
||||
description: 'Separate exported values with this string',
|
||||
|
|
63
src/ui/public/courier/fetch/strategy/__tests__/search.js
Normal file
63
src/ui/public/courier/fetch/strategy/__tests__/search.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
describe('ui/courier/fetch/strategy/search', () => {
|
||||
const _ = require('lodash');
|
||||
const sinon = require('auto-release-sinon');
|
||||
const expect = require('expect.js');
|
||||
const ngMock = require('ngMock');
|
||||
|
||||
let Promise;
|
||||
let $rootScope;
|
||||
let search;
|
||||
let reqsFetchParams;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(ngMock.inject((Private, $injector) => {
|
||||
Promise = $injector.get('Promise');
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
search = Private(require('ui/courier/fetch/strategy/search'));
|
||||
reqsFetchParams = [
|
||||
{
|
||||
index: ['logstash-123'],
|
||||
type: 'blah',
|
||||
search_type: 'blah2',
|
||||
body: 'hm this is the body'
|
||||
}
|
||||
];
|
||||
}));
|
||||
|
||||
describe('#clientMethod', () => {
|
||||
it('is msearch', () => {
|
||||
expect(search.clientMethod).to.equal('msearch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reqsFetchParamsToBody()', () => {
|
||||
context('when indexList is not empty', () => {
|
||||
it('includes the index', () => {
|
||||
let value;
|
||||
search.reqsFetchParamsToBody(reqsFetchParams).then(val => value = val);
|
||||
$rootScope.$apply();
|
||||
expect(_.includes(value, '"index":["logstash-123"]')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
context('when indexList is empty', () => {
|
||||
beforeEach(() => reqsFetchParams[0].index = []);
|
||||
|
||||
it('explicitly negates any indexes', () => {
|
||||
let value;
|
||||
search.reqsFetchParamsToBody(reqsFetchParams).then(val => value = val);
|
||||
$rootScope.$apply();
|
||||
expect(_.includes(value, '"index":["-*"]')).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getResponses()', () => {
|
||||
it('returns the `responses` property of the given arg', () => {
|
||||
const responses = [{}];
|
||||
const returned = search.getResponses({ responses });
|
||||
expect(returned).to.be(responses);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -24,6 +24,17 @@ define(function (require) {
|
|||
return indexList.toIndexList(timeBounds.min, timeBounds.max);
|
||||
})
|
||||
.then(function (indexList) {
|
||||
// If we've reached this point and there are no indexes in the
|
||||
// index list at all, it means that we shouldn't expect any indexes
|
||||
// to contain the documents we're looking for, so we instead
|
||||
// perform a request for an index pattern that we know will always
|
||||
// return an empty result (ie. -*). If instead we had gone ahead
|
||||
// with an msearch without any index patterns, elasticsearch would
|
||||
// handle that request by querying *all* indexes, which is the
|
||||
// opposite of what we want in this case.
|
||||
if (_.isArray(indexList) && indexList.length === 0) {
|
||||
indexList.push('-*');
|
||||
}
|
||||
return angular.toJson({
|
||||
index: indexList,
|
||||
type: fetchParams.type,
|
||||
|
|
57
src/ui/public/directives/__tests__/json_input.js
Normal file
57
src/ui/public/directives/__tests__/json_input.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
var angular = require('angular');
|
||||
var expect = require('expect.js');
|
||||
var ngMock = require('ngMock');
|
||||
|
||||
require('ui/directives/json_input');
|
||||
|
||||
describe('JSON input validation', function () {
|
||||
var $compile;
|
||||
var $rootScope;
|
||||
var html = '<input ng-model="value" json-input require-keys=true />';
|
||||
var element;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(ngMock.inject(function (_$compile_, _$rootScope_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
beforeEach(function () {
|
||||
element = $compile(html)($rootScope);
|
||||
});
|
||||
|
||||
it('should be able to require keys', function () {
|
||||
element.val('{}');
|
||||
element.trigger('input');
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
});
|
||||
|
||||
it('should be able to not require keys', function () {
|
||||
var html = '<input ng-model="value" json-input require-keys=false />';
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
element.val('{}');
|
||||
element.trigger('input');
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
});
|
||||
|
||||
it('should be able to read parse an input', function () {
|
||||
element.val('{}');
|
||||
element.trigger('input');
|
||||
expect($rootScope.value).to.eql({});
|
||||
});
|
||||
|
||||
it('should not allow invalid json', function () {
|
||||
element.val('{foo}');
|
||||
element.trigger('input');
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
});
|
||||
|
||||
it('should allow valid json', function () {
|
||||
element.val('{"foo": "bar"}');
|
||||
element.trigger('input');
|
||||
expect($rootScope.value).to.eql({foo: 'bar'});
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
});
|
||||
});
|
33
src/ui/public/directives/json_input.js
Normal file
33
src/ui/public/directives/json_input.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var angular = require('angular');
|
||||
|
||||
require('ui/modules')
|
||||
.get('kibana')
|
||||
.directive('jsonInput', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function (scope, $el, attrs, ngModelCntrl) {
|
||||
ngModelCntrl.$formatters.push(toJSON);
|
||||
ngModelCntrl.$parsers.push(fromJSON);
|
||||
|
||||
function fromJSON(value) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
var validity = !scope.$eval(attrs.requireKeys) ? true : _.keys(value).length > 0;
|
||||
ngModelCntrl.$setValidity('json', validity);
|
||||
} catch (e) {
|
||||
ngModelCntrl.$setValidity('json', false);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function toJSON(value) {
|
||||
return angular.toJson(value, 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
|
@ -17,7 +17,7 @@ define(function (require) {
|
|||
* Listens for events
|
||||
* @param {string} name - The name of the event
|
||||
* @param {function} handler - The function to call when the event is triggered
|
||||
* @returns {undefined}
|
||||
* @return {Events} - this, for chaining
|
||||
*/
|
||||
Events.prototype.on = function (name, handler) {
|
||||
if (!_.isArray(this._listeners[name])) {
|
||||
|
@ -46,15 +46,15 @@ define(function (require) {
|
|||
* Removes an event listener
|
||||
* @param {string} [name] - The name of the event
|
||||
* @param {function} [handler] - The handler to remove
|
||||
* @return {undefined}
|
||||
* @return {Events} - this, for chaining
|
||||
*/
|
||||
Events.prototype.off = function (name, handler) {
|
||||
if (!name && !handler) {
|
||||
return this._listeners = {};
|
||||
return this.removeAllListeners();
|
||||
}
|
||||
|
||||
// exit early if there is not an event that matches
|
||||
if (!this._listeners[name]) return;
|
||||
if (!this._listeners[name]) return this;
|
||||
|
||||
// If no hander remove all the events
|
||||
if (!handler) {
|
||||
|
|
85
src/ui/public/filter_bar/__tests__/_updateFilters.js
Normal file
85
src/ui/public/filter_bar/__tests__/_updateFilters.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
describe('update filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('auto-release-sinon');
|
||||
var expect = require('expect.js');
|
||||
var ngMock = require('ngMock');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var queryFilter;
|
||||
var appState;
|
||||
var globalState;
|
||||
var $rootScope;
|
||||
|
||||
beforeEach(ngMock.module(
|
||||
'kibana',
|
||||
'kibana/courier',
|
||||
'kibana/global_state',
|
||||
function ($provide) {
|
||||
$provide.service('courier', require('fixtures/mock_courier'));
|
||||
|
||||
appState = new MockState({ filters: [] });
|
||||
$provide.service('getAppState', function () {
|
||||
return function () { return appState; };
|
||||
});
|
||||
|
||||
globalState = new MockState({ filters: [] });
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
beforeEach(ngMock.inject(function (Private, _$rootScope_) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('ui/filter_bar/query_filter'));
|
||||
}));
|
||||
|
||||
describe('updating', function () {
|
||||
var currentFilter;
|
||||
|
||||
beforeEach(function () {
|
||||
currentFilter = {query: { match: { extension: { query: 'jpg', type: 'phrase' } } } };
|
||||
});
|
||||
|
||||
it('should be able to update a filter', function () {
|
||||
var newFilter = _.cloneDeep(currentFilter);
|
||||
newFilter.query.match.extension.query = 'png';
|
||||
|
||||
expect(currentFilter.query.match.extension.query).to.be('jpg');
|
||||
queryFilter.updateFilter({
|
||||
source: currentFilter,
|
||||
model: newFilter
|
||||
});
|
||||
$rootScope.$digest();
|
||||
expect(currentFilter.query.match.extension.query).to.be('png');
|
||||
});
|
||||
|
||||
it('should replace the filter type if it is changed', function () {
|
||||
var newFilter = {
|
||||
'range': {
|
||||
'bytes': {
|
||||
'gte': 0,
|
||||
'lt': 1000
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(currentFilter.query).not.to.be(undefined);
|
||||
|
||||
queryFilter.updateFilter({
|
||||
source: currentFilter,
|
||||
model: newFilter,
|
||||
type: 'query'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(currentFilter.query).to.be(undefined);
|
||||
expect(currentFilter.range).not.to.be(undefined);
|
||||
expect(_.eq(currentFilter.range, newFilter.range)).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
var ngMock = require('ngMock');
|
||||
var expect = require('expect.js');
|
||||
var sinon = require('sinon');
|
||||
|
||||
require('ui/filter_bar');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
|
@ -17,6 +17,7 @@ describe('Filter Bar Directive', function () {
|
|||
var queryFilter;
|
||||
var mapFilter;
|
||||
var $el;
|
||||
var $scope;
|
||||
// require('testUtils/noDigestPromises').activateForSuite();
|
||||
|
||||
beforeEach(ngMock.module('kibana/global_state', function ($provide) {
|
||||
|
@ -59,6 +60,7 @@ describe('Filter Bar Directive', function () {
|
|||
Promise.map(filters, mapFilter).then(function (filters) {
|
||||
appState.filters = filters;
|
||||
$el = $compile('<filter-bar></filter-bar>')($rootScope);
|
||||
$scope = $el.isolateScope();
|
||||
});
|
||||
|
||||
var off = $rootScope.$on('filterbar:updated', function () {
|
||||
|
@ -83,5 +85,29 @@ describe('Filter Bar Directive', function () {
|
|||
expect($(filters[3]).find('span')[0].innerHTML).to.equal('missing:');
|
||||
expect($(filters[3]).find('span')[1].innerHTML).to.equal('"host"');
|
||||
});
|
||||
|
||||
describe('editing filters', function () {
|
||||
beforeEach(function () {
|
||||
$scope.startEditingFilter(appState.filters[3]);
|
||||
$scope.$digest();
|
||||
});
|
||||
|
||||
it('should be able to edit a filter', function () {
|
||||
expect($el.find('.filter-edit-container').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should be able to stop editing a filter', function () {
|
||||
$scope.stopEditingFilter();
|
||||
$scope.$digest();
|
||||
expect($el.find('.filter-edit-container').length).to.be(0);
|
||||
});
|
||||
|
||||
it('should merge changes after clicking done', function () {
|
||||
sinon.spy($scope, 'updateFilter');
|
||||
|
||||
$scope.editDone();
|
||||
expect($scope.updateFilter.called).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,6 +33,7 @@ describe('Query Filter', function () {
|
|||
expect(queryFilter.toggleAll).to.be.a('function');
|
||||
expect(queryFilter.removeFilter).to.be.a('function');
|
||||
expect(queryFilter.removeAll).to.be.a('function');
|
||||
expect(queryFilter.updateFilter).to.be.a('function');
|
||||
expect(queryFilter.invertFilter).to.be.a('function');
|
||||
expect(queryFilter.invertAll).to.be.a('function');
|
||||
expect(queryFilter.pinFilter).to.be.a('function');
|
||||
|
@ -45,6 +46,7 @@ describe('Query Filter', function () {
|
|||
require('./_getFilters');
|
||||
require('./_addFilters');
|
||||
require('./_removeFilters');
|
||||
require('./_updateFilters');
|
||||
require('./_toggleFilters');
|
||||
require('./_invertFilters');
|
||||
require('./_pinFilters');
|
||||
|
|
|
@ -32,8 +32,32 @@
|
|||
<a class="action filter-remove" ng-click="removeFilter(filter)">
|
||||
<i class="fa fa-fw fa-trash"></i>
|
||||
</a>
|
||||
<a class="action filter-edit" ng-click="startEditingFilter(filter)">
|
||||
<i class="fa fa-fw fa-edit"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-edit-container" ng-if="editingFilter">
|
||||
<form role="form" name="editFilterForm" ng-submit="editDone()">
|
||||
<div
|
||||
json-input
|
||||
require-keys="true"
|
||||
ui-ace="{
|
||||
mode: 'json',
|
||||
onLoad: aceLoaded
|
||||
}"
|
||||
ng-model="editingFilter.model"></div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" ng-click="stopEditingFilter()">Cancel</button>
|
||||
<button type="submit" class="btn btn-success"
|
||||
ng-disabled="editFilterForm.$invalid"
|
||||
>Done</button>
|
||||
<small ng-show="editFilterForm.$invalid">Could not parse JSON input</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="filter-link">
|
||||
<div class="filter-description small">
|
||||
<a ng-click="showFilterActions = !showFilterActions">
|
||||
|
|
|
@ -3,6 +3,9 @@ define(function (require) {
|
|||
var module = require('ui/modules').get('kibana');
|
||||
var template = require('ui/filter_bar/filter_bar.html');
|
||||
var moment = require('moment');
|
||||
var angular = require('angular');
|
||||
|
||||
require('ui/directives/json_input');
|
||||
|
||||
module.directive('filterBar', function (Private, Promise, getAppState) {
|
||||
var mapAndFlattenFilters = Private(require('ui/filter_bar/lib/mapAndFlattenFilters'));
|
||||
|
@ -28,13 +31,21 @@ define(function (require) {
|
|||
'invertFilter',
|
||||
'invertAll',
|
||||
'removeFilter',
|
||||
'removeAll'
|
||||
'removeAll',
|
||||
'updateFilter'
|
||||
].forEach(function (method) {
|
||||
$scope[method] = queryFilter[method];
|
||||
});
|
||||
|
||||
$scope.state = getAppState();
|
||||
|
||||
$scope.aceLoaded = function (editor) {
|
||||
editor.$blockScrolling = Infinity;
|
||||
var session = editor.getSession();
|
||||
session.setTabSize(2);
|
||||
session.setUseSoftTabs(true);
|
||||
};
|
||||
|
||||
$scope.applyFilters = function (filters) {
|
||||
// add new filters
|
||||
$scope.addFilters(filterAppliedAndUnwrap(filters));
|
||||
|
@ -46,6 +57,36 @@ define(function (require) {
|
|||
}
|
||||
};
|
||||
|
||||
var privateFieldRegexp = /(^\$|meta)/;
|
||||
$scope.startEditingFilter = function (source) {
|
||||
var model = _.cloneDeep(source);
|
||||
var filterType;
|
||||
|
||||
//Hide private properties and figure out what type of filter this is
|
||||
_.each(model, function (value, key) {
|
||||
if (key.match(privateFieldRegexp)) {
|
||||
delete model[key];
|
||||
} else {
|
||||
filterType = key;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.editingFilter = {
|
||||
source: source,
|
||||
type: filterType,
|
||||
model: model
|
||||
};
|
||||
};
|
||||
|
||||
$scope.stopEditingFilter = function () {
|
||||
$scope.editingFilter = null;
|
||||
};
|
||||
|
||||
$scope.editDone = function () {
|
||||
$scope.updateFilter($scope.editingFilter);
|
||||
$scope.stopEditingFilter();
|
||||
};
|
||||
|
||||
$scope.clearFilterBar = function () {
|
||||
$scope.newFilters = [];
|
||||
$scope.changeTimeFilter = null;
|
||||
|
@ -53,6 +94,7 @@ define(function (require) {
|
|||
|
||||
// update the scope filter list on filter changes
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
$scope.stopEditingFilter();
|
||||
updateFilters();
|
||||
});
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ filter-bar .confirm {
|
|||
position: relative;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
min-width: 110px;
|
||||
min-width: 140px;
|
||||
|
||||
font-size: @font-size-small;
|
||||
background-color: @filter-bar-confirm-filter-bg;
|
||||
|
@ -55,6 +55,11 @@ filter-bar .bar {
|
|||
background: @filter-bar-bar-condensed-bg;
|
||||
}
|
||||
|
||||
.ace_editor {
|
||||
height: 175px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.filter-link {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -72,7 +77,7 @@ filter-bar .bar {
|
|||
position: relative;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
min-width: 110px;
|
||||
min-width: 140px;
|
||||
|
||||
font-size: @font-size-small;
|
||||
background-color: @filter-bar-bar-filter-bg;
|
||||
|
|
|
@ -21,8 +21,26 @@ describe('Filter Bar Directive', function () {
|
|||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
it('should return undefined for none matching', function (done) {
|
||||
var filter = { range: { gt: 0, lt: 1024 } };
|
||||
it('should work with undefined filter types', function (done) {
|
||||
var filter = {
|
||||
'bool': {
|
||||
'must': {
|
||||
'term': {
|
||||
'geo.src': 'US'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mapDefault(filter).then(function (result) {
|
||||
expect(result).to.have.property('key', 'bool');
|
||||
expect(result).to.have.property('value', JSON.stringify(filter.bool));
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
it('should return undefined if there is no valid key', function (done) {
|
||||
var filter = { meta: {} };
|
||||
mapDefault(filter).catch(function (result) {
|
||||
expect(result).to.be(filter);
|
||||
done();
|
||||
|
|
|
@ -74,7 +74,7 @@ describe('Filter Bar Directive', function () {
|
|||
});
|
||||
|
||||
it('should finish with a catch', function (done) {
|
||||
var before = { meta: { index: 'logstash-*' }, foo: '' };
|
||||
var before = { meta: { index: 'logstash-*' }};
|
||||
mapFilter(before).catch(function (error) {
|
||||
expect(error).to.be.an(Error);
|
||||
expect(error.message).to.be('No mappings have been found for filter.');
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
define(function (require) {
|
||||
return function mapDefaultProvider(Promise) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
var metaProperty = /(^\$|meta)/;
|
||||
|
||||
return function (filter) {
|
||||
var key;
|
||||
var value;
|
||||
if (filter.query) {
|
||||
key = 'query';
|
||||
value = angular.toJson(filter.query);
|
||||
var key = _.find(_.keys(filter), function (key) {
|
||||
return !key.match(metaProperty);
|
||||
});
|
||||
|
||||
if (key) {
|
||||
var value = angular.toJson(filter[key]);
|
||||
return Promise.resolve({ key: key, value: value });
|
||||
}
|
||||
return Promise.reject(filter);
|
||||
|
|
|
@ -8,6 +8,7 @@ define(function (require) {
|
|||
var uniqFilters = require('ui/filter_bar/lib/uniqFilters');
|
||||
var compareFilters = require('ui/filter_bar/lib/compareFilters');
|
||||
var mapAndFlattenFilters = Private(require('ui/filter_bar/lib/mapAndFlattenFilters'));
|
||||
var angular = require('angular');
|
||||
|
||||
var queryFilter = new EventEmitter();
|
||||
|
||||
|
@ -82,6 +83,26 @@ define(function (require) {
|
|||
state.filters.splice(index, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing filter
|
||||
* @param {object} filter Contains a reference to a filter and its new model
|
||||
* @param {object} filter.source The filter reference
|
||||
* @param {string} filter.model The edited filter
|
||||
* @returns {object} Promise that resolves to the new filter on a successful merge
|
||||
*/
|
||||
queryFilter.updateFilter = function (filter) {
|
||||
var mergedFilter = _.assign({}, filter.source, filter.model);
|
||||
|
||||
//If the filter type is changed we want to discard the old type
|
||||
//when merging changes back in
|
||||
var filterTypeReplaced = filter.model[filter.type] !== mergedFilter[filter.type];
|
||||
if (filterTypeReplaced) {
|
||||
delete mergedFilter[filter.type];
|
||||
}
|
||||
|
||||
return angular.copy(mergedFilter, filter.source);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all filters
|
||||
*/
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
140
src/ui/public/images/kibana.svg
Normal file
140
src/ui/public/images/kibana.svg
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="252px"
|
||||
height="45px"
|
||||
viewBox="0 0 252 45"
|
||||
enable-background="new 0 0 252 45"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="kibana.svg"><metadata
|
||||
id="metadata4270"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4268" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2046"
|
||||
inkscape:window-height="1132"
|
||||
id="namedview4266"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0416667"
|
||||
inkscape:cx="126"
|
||||
inkscape:cy="22.5"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" /><font
|
||||
horiz-adv-x="1000"
|
||||
id="font4232"><!-- "Helvetica Neue" is a trademark of Heidelberger Druckmaschinen AG, which may be registered in certain jurisdictions, exclusivly licensed through Linotype Library GmbH, a wholly owned subsidiary of Heidelberger Druckmaschinen AG. --><font-face
|
||||
font-family="HelveticaNeue-Bold"
|
||||
units-per-em="1000"
|
||||
underline-position="-100"
|
||||
underline-thickness="50"
|
||||
id="font-face4234" /><missing-glyph
|
||||
horiz-adv-x="500"
|
||||
d="M391,607l-280,0l0,-512l280,0M482,698l0,-695l-465,0l0,695z"
|
||||
id="missing-glyph4236" /><glyph
|
||||
unicode="a"
|
||||
horiz-adv-x="574"
|
||||
d="M48,358C50,391 58,419 73,441C88,463 106,481 129,494C152,507 177,517 206,523C234,528 262,531 291,531C317,531 343,529 370,526C397,522 421,515 443,504C465,493 483,479 497,460C511,441 518,415 518,384l0,-269C518,92 519,69 522,48C525,27 529,11 536,0l-144,0C389,8 387,16 386,25C384,33 383,41 382,50C359,27 333,10 302,1C271,-8 240,-13 208,-13C183,-13 160,-10 139,-4C118,2 99,11 83,24C67,37 55,53 46,72C37,91 32,114 32,141C32,170 37,195 48,214C58,233 71,248 88,259C104,270 123,279 144,285C165,290 186,295 207,298C228,301 249,304 270,306C291,308 309,311 325,315C341,319 354,325 363,333C372,340 377,351 376,366C376,381 374,394 369,403C364,412 357,419 349,424C340,429 331,432 320,434C309,435 297,436 284,436C256,436 234,430 218,418C202,406 193,386 190,358M376,253C370,248 363,244 354,241C345,238 335,235 325,233C314,231 303,229 292,228C281,227 269,225 258,223C247,221 237,218 227,215C216,212 207,207 200,202C192,196 186,189 181,180C176,171 174,160 174,147C174,134 176,124 181,115C186,106 192,100 200,95C208,90 217,86 228,84C239,82 250,81 261,81C289,81 311,86 326,95C341,104 353,116 360,129C367,142 372,155 374,168C375,181 376,192 376,200z"
|
||||
id="glyph4238" /><glyph
|
||||
unicode="b"
|
||||
horiz-adv-x="611"
|
||||
d="M433,258C433,237 431,216 426,196C421,176 414,158 404,143C394,128 382,116 367,107C352,98 333,93 312,93C291,93 273,98 258,107C243,116 230,128 220,143C210,158 203,176 198,196C193,216 191,237 191,258C191,280 193,301 198,321C203,341 210,359 220,374C230,389 243,402 258,411C273,420 291,424 312,424C333,424 352,420 367,411C382,402 394,389 404,374C414,359 421,341 426,321C431,301 433,280 433,258M54,714l0,-714l135,0l0,66l2,0C206,37 229,17 259,5C289,-7 323,-13 361,-13C387,-13 413,-8 438,3C463,14 486,30 507,52C527,74 543,102 556,137C569,171 575,212 575,259C575,306 569,347 556,382C543,416 527,444 507,466C486,488 463,504 438,515C413,526 387,531 361,531C329,531 298,525 269,513C239,500 215,481 198,454l-2,0l0,260z"
|
||||
id="glyph4240" /><glyph
|
||||
unicode="i"
|
||||
horiz-adv-x="258"
|
||||
d="M200,597l0,117l-142,0l0,-117M58,517l0,-517l142,0l0,517z"
|
||||
id="glyph4242" /><glyph
|
||||
unicode="k"
|
||||
horiz-adv-x="574"
|
||||
d="M67,714l0,-714l142,0l0,178l55,53l142,-231l172,0l-217,327l195,190l-168,0l-179,-186l0,383z"
|
||||
id="glyph4244" /><glyph
|
||||
unicode="n"
|
||||
horiz-adv-x="593"
|
||||
d="M54,517l0,-517l142,0l0,271C196,324 205,362 222,385C239,408 267,419 306,419C340,419 364,409 377,388C390,367 397,335 397,292l0,-292l142,0l0,318C539,350 536,379 531,406C525,432 515,454 501,473C487,491 468,505 444,516C419,526 388,531 350,531C320,531 291,524 262,511C233,497 210,475 192,445l-3,0l0,72z"
|
||||
id="glyph4246" /></font><rect
|
||||
fill="#3C3C3C"
|
||||
width="252"
|
||||
height="45"
|
||||
id="rect4248"
|
||||
style="opacity:1" /><rect
|
||||
fill="#85C441"
|
||||
width="6.094"
|
||||
height="45"
|
||||
id="rect4250"
|
||||
style="opacity:1" /><rect
|
||||
x="5.958"
|
||||
fill="#2C448E"
|
||||
width="6.094"
|
||||
height="45"
|
||||
id="rect4252"
|
||||
style="opacity:1" /><rect
|
||||
x="12.052"
|
||||
fill="#F2BB1A"
|
||||
width="9.847"
|
||||
height="45"
|
||||
id="rect4254"
|
||||
style="opacity:1" /><rect
|
||||
x="21.899"
|
||||
fill="#3BBEB1"
|
||||
width="6.068"
|
||||
height="45"
|
||||
id="rect4256"
|
||||
style="opacity:1" /><rect
|
||||
x="28.029"
|
||||
fill="#006656"
|
||||
width="2.84"
|
||||
height="45"
|
||||
id="rect4258"
|
||||
style="opacity:1" /><rect
|
||||
x="30.869"
|
||||
fill="#EA458B"
|
||||
width="15.006"
|
||||
height="45"
|
||||
id="rect4260"
|
||||
style="opacity:1" /><rect
|
||||
x="41.5"
|
||||
fill="none"
|
||||
width="207.5"
|
||||
height="74"
|
||||
id="rect4262" /><g
|
||||
style="font-size:64.99947357px;font-family:HelveticaNeue-Bold;letter-spacing:-2.99203849px;opacity:1;fill:#ffffff"
|
||||
id="text4264"><path
|
||||
d="m 45.721,-0.49421886 0,46.40962386 9.229925,0 0,-11.569906 3.574971,-3.444972 9.229926,15.014878 11.179909,0 -14.104886,-21.254828 12.674898,-12.3499 -10.919912,0 -11.634906,12.089903 0,-24.89479886 -9.229925,0 z"
|
||||
style="letter-spacing:-1.33094156px"
|
||||
id="path4912" /><path
|
||||
d="m 90.358907,7.1107196 0,-7.60493846 -9.229925,0 0,7.60493846 9.229925,0 z m -9.229925,5.1999574 0,33.604728 9.229925,0 0,-33.604728 -9.229925,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
|
||||
id="path4914" /><path
|
||||
d="m 119.38291,29.145541 q 0,2.079983 -0.45499,4.029967 -0.455,1.949985 -1.42999,3.444973 -0.97499,1.494987 -2.46998,2.40498 -1.42999,0.844993 -3.50997,0.844993 -2.01499,0 -3.50998,-0.844993 -1.49498,-0.909993 -2.46998,-2.40498 -0.97499,-1.494988 -1.42998,-3.444973 -0.455,-1.949984 -0.455,-4.029967 0,-2.144983 0.455,-4.094967 0.45499,-1.949984 1.42998,-3.444972 0.975,-1.494988 2.46998,-2.339981 1.49499,-0.909993 3.50998,-0.909993 2.07998,0 3.50997,0.909993 1.49499,0.844993 2.46998,2.339981 0.97499,1.494988 1.42999,3.444972 0.45499,1.949984 0.45499,4.094967 z m -24.634798,-29.63975986 0,46.40962386 8.774928,0 0,-4.289965 0.13,0 q 1.49499,2.794977 4.41996,3.964968 2.92498,1.16999 6.62995,1.16999 2.53498,0 5.00496,-1.039991 2.46998,-1.039992 4.41996,-3.184974 2.01499,-2.144983 3.24998,-5.459956 1.23499,-3.379973 1.23499,-7.994935 0,-4.614963 -1.23499,-7.929936 -1.23499,-3.379973 -3.24998,-5.524955 -1.94998,-2.144983 -4.41996,-3.184975 -2.46998,-1.039991 -5.00496,-1.039991 -3.11997,0 -6.04495,1.23499 -2.85998,1.16999 -4.54996,3.769969 l -0.13,0 0,-16.89986286 -9.229928,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
|
||||
id="path4916" /><path
|
||||
d="m 131.21557,22.645594 q 0.195,-3.249974 1.62499,-5.394957 1.42999,-2.144982 3.63997,-3.444972 2.20998,-1.299989 4.93996,-1.819985 2.79498,-0.584995 5.58995,-0.584995 2.53498,0 5.13496,0.389997 2.59998,0.324997 4.74496,1.364989 2.14499,1.039991 3.50997,2.924976 1.36499,1.819985 1.36499,4.87496 l 0,17.484859 q 0,2.274981 0.26,4.354965 0.26,2.079983 0.90999,3.119974 l -9.35992,0 q -0.26,-0.779993 -0.455,-1.559987 -0.13,-0.844993 -0.19499,-1.689986 -2.20999,2.274981 -5.19996,3.184974 -2.98998,0.909992 -6.10995,0.909992 -2.40498,0 -4.48497,-0.584995 -2.07998,-0.584995 -3.63997,-1.819985 -1.55998,-1.23499 -2.46998,-3.119975 -0.84499,-1.884985 -0.84499,-4.484964 0,-2.859976 0.97499,-4.679962 1.03999,-1.884984 2.59998,-2.989975 1.62499,-1.104991 3.63997,-1.624987 2.07998,-0.584995 4.15997,-0.909993 2.07998,-0.324997 4.09496,-0.519996 2.01499,-0.194998 3.57498,-0.584995 1.55998,-0.389997 2.46998,-1.104991 0.90999,-0.779994 0.84499,-2.209982 0,-1.494988 -0.52,-2.339981 -0.45499,-0.909993 -1.29999,-1.364989 -0.77999,-0.519996 -1.88498,-0.649995 -1.03999,-0.194998 -2.27498,-0.194998 -2.72998,0 -4.28997,1.16999 -1.55999,1.169991 -1.81998,3.899969 l -9.22993,0 z m 21.31983,6.824944 q -0.585,0.519996 -1.49499,0.844994 -0.84499,0.259998 -1.88498,0.454996 -0.975,0.194998 -2.07999,0.324997 -1.10499,0.129999 -2.20998,0.324998 -1.03999,0.194998 -2.07998,0.519995 -0.97499,0.324998 -1.75499,0.909993 -0.71499,0.519996 -1.16999,1.364989 -0.455,0.844993 -0.455,2.144983 0,1.23499 0.455,2.079983 0.455,0.844993 1.23499,1.364989 0.77999,0.454996 1.81999,0.649994 1.03999,0.194999 2.14498,0.194999 2.72998,0 4.22496,-0.909993 1.49499,-0.909992 2.20999,-2.144982 0.71499,-1.29999 0.84499,-2.599979 0.195,-1.29999 0.195,-2.079983 l 0,-3.444973 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
|
||||
id="path4918" /><path
|
||||
d="m 165.92399,12.310677 0,33.604728 9.22992,0 0,-17.614857 q 0,-5.134958 1.68999,-7.344941 1.68999,-2.274981 5.45996,-2.274981 3.31497,0 4.61496,2.079983 1.29999,2.014984 1.29999,6.17495 l 0,18.979846 9.22992,0 0,-20.669832 q 0,-3.119975 -0.58499,-5.654955 -0.52,-2.599978 -1.88499,-4.354964 -1.36499,-1.819986 -3.76997,-2.794978 -2.33998,-1.039991 -6.04495,-1.039991 -2.92497,0 -5.71995,1.364989 -2.79498,1.299989 -4.54996,4.224966 l -0.195,0 0,-4.679963 -8.77493,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
|
||||
id="path4920" /><path
|
||||
d="m 201.24887,22.645594 q 0.195,-3.249974 1.62499,-5.394957 1.42999,-2.144982 3.63997,-3.444972 2.20998,-1.299989 4.93996,-1.819985 2.79498,-0.584995 5.58996,-0.584995 2.53497,0 5.13495,0.389997 2.59998,0.324997 4.74497,1.364989 2.14498,1.039991 3.50997,2.924976 1.36499,1.819985 1.36499,4.87496 l 0,17.484859 q 0,2.274981 0.25999,4.354965 0.26,2.079983 0.91,3.119974 l -9.35993,0 q -0.26,-0.779993 -0.45499,-1.559987 -0.13,-0.844993 -0.195,-1.689986 -2.20998,2.274981 -5.19996,3.184974 -2.98998,0.909992 -6.10995,0.909992 -2.40498,0 -4.48496,-0.584995 -2.07999,-0.584995 -3.63998,-1.819985 -1.55998,-1.23499 -2.46998,-3.119975 -0.84499,-1.884985 -0.84499,-4.484964 0,-2.859976 0.97499,-4.679962 1.04,-1.884984 2.59998,-2.989975 1.62499,-1.104991 3.63997,-1.624987 2.07999,-0.584995 4.15997,-0.909993 2.07998,-0.324997 4.09497,-0.519996 2.01498,-0.194998 3.57497,-0.584995 1.55998,-0.389997 2.46998,-1.104991 0.90999,-0.779994 0.84499,-2.209982 0,-1.494988 -0.52,-2.339981 -0.45499,-0.909993 -1.29998,-1.364989 -0.78,-0.519996 -1.88499,-0.649995 -1.03999,-0.194998 -2.27498,-0.194998 -2.72998,0 -4.28997,1.16999 -1.55998,1.169991 -1.81998,3.899969 l -9.22993,0 z m 21.31983,6.824944 q -0.58499,0.519996 -1.49499,0.844994 -0.84499,0.259998 -1.88498,0.454996 -0.97499,0.194998 -2.07999,0.324997 -1.10499,0.129999 -2.20998,0.324998 -1.03999,0.194998 -2.07998,0.519995 -0.97499,0.324998 -1.75499,0.909993 -0.71499,0.519996 -1.16999,1.364989 -0.45499,0.844993 -0.45499,2.144983 0,1.23499 0.45499,2.079983 0.455,0.844993 1.23499,1.364989 0.78,0.454996 1.81999,0.649994 1.03999,0.194999 2.14498,0.194999 2.72998,0 4.22497,-0.909993 1.49498,-0.909992 2.20998,-2.144982 0.71499,-1.29999 0.84499,-2.599979 0.195,-1.29999 0.195,-2.079983 l 0,-3.444973 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:-2.87854815px"
|
||||
id="path4922" /></g></svg>
|
After Width: | Height: | Size: 12 KiB |
|
@ -50,21 +50,21 @@ describe('ui/index_patterns/_calculate_indices', () => {
|
|||
|
||||
context('when given start', () => {
|
||||
beforeEach(() => run({ start: '1234567890' }));
|
||||
it('includes min_value', () => {
|
||||
expect(constraints['@something']).to.have.property('min_value');
|
||||
it('includes max_value', () => {
|
||||
expect(constraints['@something']).to.have.property('max_value');
|
||||
});
|
||||
it('min_value is gte', () => {
|
||||
expect(constraints['@something'].min_value).to.have.property('gte');
|
||||
it('max_value is gte', () => {
|
||||
expect(constraints['@something'].max_value).to.have.property('gte');
|
||||
});
|
||||
});
|
||||
|
||||
context('when given stop', () => {
|
||||
beforeEach(() => run({ stop: '1234567890' }));
|
||||
it('includes max_value', () => {
|
||||
expect(constraints['@something']).to.have.property('max_value');
|
||||
it('includes min_value', () => {
|
||||
expect(constraints['@something']).to.have.property('min_value');
|
||||
});
|
||||
it('max_value is lt', () => {
|
||||
expect(constraints['@something'].max_value).to.have.property('lt');
|
||||
it('min_value is lte', () => {
|
||||
expect(constraints['@something'].min_value).to.have.property('lte');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,10 +17,10 @@ define(function (require) {
|
|||
function compileOptions(pattern, timeFieldName, start, stop) {
|
||||
const constraints = {};
|
||||
if (start) {
|
||||
constraints.min_value = { gte: moment(start).valueOf() };
|
||||
constraints.max_value = { gte: moment(start).valueOf() };
|
||||
}
|
||||
if (stop) {
|
||||
constraints.max_value = { lt: moment(stop).valueOf() };
|
||||
constraints.min_value = { lte: moment(stop).valueOf() };
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -20,7 +20,7 @@ define(function (require) {
|
|||
fields: [],
|
||||
body: {
|
||||
query: { match_all: {} },
|
||||
size: 2147483647
|
||||
size: 10000
|
||||
}
|
||||
})
|
||||
.then(function (resp) {
|
||||
|
|
|
@ -192,6 +192,125 @@ describe('Persisted State', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('child state removal', function () {
|
||||
it('should clear path from parent state', function () {
|
||||
var persistedState = new PersistedState();
|
||||
var childState = persistedState.createChild('child', { userId: 1234 });
|
||||
expect(persistedState.get()).to.eql({ child: { userId: 1234 }});
|
||||
persistedState.removeChild('child');
|
||||
expect(persistedState.get()).to.eql({});
|
||||
});
|
||||
|
||||
it('should reset original parent value at path', function () {
|
||||
var persistedState = new PersistedState({ user: 1234 });
|
||||
var childState = persistedState.createChild('user', { id: 5678 });
|
||||
expect(persistedState.get()).to.eql({ user: { id: 5678 }});
|
||||
|
||||
persistedState.removeChild('user');
|
||||
expect(persistedState.get()).to.eql({ user: 1234 });
|
||||
});
|
||||
|
||||
it('should clear changedState', function () {
|
||||
var persistedState = new PersistedState({ user: 1234 });
|
||||
var childState = persistedState.createChild('user');
|
||||
childState.set('name', 'user name');
|
||||
expect(persistedState.getChanges()).to.eql({ user: { name: 'user name' }});
|
||||
|
||||
persistedState.removeChild('user');
|
||||
expect(persistedState.getChanges()).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deep child state removal', function () {
|
||||
it('should clear path from parent state', function () {
|
||||
var persistedState = new PersistedState();
|
||||
var childState = persistedState.createChild('child.state', { userId: 1234 });
|
||||
expect(persistedState.get()).to.eql({ child: { state: { userId: 1234 }}});
|
||||
persistedState.removeChild('child.state');
|
||||
expect(persistedState.get()).to.eql({});
|
||||
});
|
||||
|
||||
it('should reset original parent value at path', function () {
|
||||
var persistedState = new PersistedState({ user: { id: 1234 }});
|
||||
var childState = persistedState.createChild('user.id', 5678);
|
||||
expect(persistedState.get()).to.eql({ user: { id: 5678 }});
|
||||
|
||||
persistedState.removeChild('user.id');
|
||||
expect(persistedState.get()).to.eql({ user: { id: 1234 }});
|
||||
});
|
||||
|
||||
it('should reset original parent other values at path', function () {
|
||||
var persistedState = new PersistedState({ user: { name: 'user' }});
|
||||
var childState = persistedState.createChild('user.id', 5678);
|
||||
expect(persistedState.get()).to.eql({ user: { name: 'user', id: 5678 }});
|
||||
|
||||
persistedState.removeChild('user.id');
|
||||
expect(persistedState.get()).to.eql({ user: { name: 'user' }});
|
||||
});
|
||||
|
||||
it('should clear the changed state', function () {
|
||||
var persistedState = new PersistedState({ user: { id: 1234 }});
|
||||
var childState = persistedState.createChild('user.name');
|
||||
childState.set('user name');
|
||||
expect(persistedState.getChanges()).to.eql({ user: { name: 'user name' }});
|
||||
|
||||
persistedState.removeChild('user.name');
|
||||
expect(persistedState.getChanges()).to.eql({});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('child state conditions', function () {
|
||||
it('should be merged with the parent state', function () {
|
||||
var parent = new PersistedState({ name: 'test' });
|
||||
var child = parent.createChild('child', 'value');
|
||||
expect(parent.get()).to.eql({
|
||||
name: 'test',
|
||||
child: 'value'
|
||||
});
|
||||
|
||||
parent.set('id', 1234);
|
||||
expect(parent.get()).to.eql({
|
||||
id: 1234,
|
||||
name: 'test',
|
||||
child: 'value'
|
||||
});
|
||||
|
||||
parent.set({});
|
||||
expect(parent.get()).to.eql({
|
||||
child: 'value'
|
||||
});
|
||||
});
|
||||
|
||||
it('should give child state precedence', function () {
|
||||
var parent = new PersistedState({ user: { id: 1234, name: 'test' }});
|
||||
var child = parent.createChild('user', { name: 'child test' });
|
||||
expect(parent.get()).to.eql({
|
||||
user: {
|
||||
id: 1234,
|
||||
name: 'child test'
|
||||
}
|
||||
});
|
||||
|
||||
parent.set({});
|
||||
expect(parent.get()).to.eql({ user: { name: 'child test' }});
|
||||
});
|
||||
|
||||
it('should be cleaned up with removeChild', function () {
|
||||
var parent = new PersistedState({ name: 'test' });
|
||||
var child = parent.createChild('child', 'value');
|
||||
expect(parent.get()).to.eql({
|
||||
name: 'test',
|
||||
child: 'value'
|
||||
});
|
||||
|
||||
parent.removeChild('child');
|
||||
expect(parent.get()).to.eql({
|
||||
name: 'test'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('colliding child paths and parent state values', function () {
|
||||
it('should not change the child path value by default', function () {
|
||||
var childIndex = 'childTest';
|
||||
|
@ -226,7 +345,7 @@ describe('Persisted State', function () {
|
|||
// pass in child state value
|
||||
var childState = persistedState.createChild(childIndex, childStateValue);
|
||||
|
||||
// parent's default state overrides child state
|
||||
// parent's default state is merged with child state
|
||||
var compare = _.merge({}, childStateValue, persistedStateValue[childIndex]);
|
||||
expect(childState.get()).to.eql(compare);
|
||||
state = persistedState.get();
|
||||
|
@ -299,7 +418,7 @@ describe('Persisted State', function () {
|
|||
});
|
||||
|
||||
describe('get state', function () {
|
||||
it('should perform deep gets with vairous formats', function () {
|
||||
it('should perform deep gets with various formats', function () {
|
||||
var obj = {
|
||||
red: {
|
||||
green: {
|
||||
|
@ -336,6 +455,15 @@ describe('Persisted State', function () {
|
|||
expect(persistedState.get('hello')).to.eql({ nouns: ['world', 'humans', 'everyone'] });
|
||||
expect(persistedState.get('hello.nouns')).to.eql(['world', 'humans', 'everyone']);
|
||||
});
|
||||
|
||||
it('should pass defaults to parent delegation', function () {
|
||||
var persistedState = new PersistedState({ parent: true });
|
||||
var childState = persistedState.createChild('child', { account: { name: 'first child' }});
|
||||
var defaultValue = 'i have no data';
|
||||
|
||||
expect(childState.get('account.name', defaultValue)).to.eql('first child');
|
||||
expect(childState.get('account.age', defaultValue)).to.eql(defaultValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set state', function () {
|
||||
|
@ -471,12 +599,24 @@ describe('Persisted State', function () {
|
|||
expect(getByType('set')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should not emit when setting value silently', function () {
|
||||
expect(getByType('set')).to.have.length(0);
|
||||
persistedState.setSilent('checker.time', 'now');
|
||||
expect(getByType('set')).to.have.length(0);
|
||||
});
|
||||
|
||||
it('should emit change when changing values', function () {
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
persistedState.set('checker.time', 'now');
|
||||
expect(getByType('change')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should not emit when changing values silently', function () {
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
persistedState.setSilent('checker.time', 'now');
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
});
|
||||
|
||||
it('should not emit change when values are identical', function () {
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
// check both forms of setting the same value
|
||||
|
@ -512,6 +652,12 @@ describe('Persisted State', function () {
|
|||
expect(getByType('change')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should not emit when createChild set to silent', function () {
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
persistedState.createChild('checker', { events: 'changed via child' }, true);
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
});
|
||||
|
||||
it('should emit change when createChild adds new value', function () {
|
||||
expect(getByType('change')).to.have.length(0);
|
||||
persistedState.createChild('new.path', { another: 'thing' });
|
||||
|
|
|
@ -22,6 +22,21 @@ define(function (require) {
|
|||
if (!_.isPlainObject(value)) throw new errors.PersistedStateError(msg);
|
||||
}
|
||||
|
||||
function prepSetParams(key, value, path) {
|
||||
// key must be the value, set the entire state using it
|
||||
if (_.isUndefined(value) && (_.isPlainObject(key) || path.length > 0)) {
|
||||
// setting entire tree, swap the key and value to write to the state
|
||||
value = key;
|
||||
key = undefined;
|
||||
}
|
||||
|
||||
// ensure the value being passed in is never mutated
|
||||
return {
|
||||
value: _.cloneDeep(value),
|
||||
key: key
|
||||
};
|
||||
}
|
||||
|
||||
function parentDelegationMixin(from, to) {
|
||||
_.forOwn(from.prototype, function (method, methodName) {
|
||||
to.prototype[methodName] = function () {
|
||||
|
@ -34,7 +49,7 @@ define(function (require) {
|
|||
parentDelegationMixin(SimpleEmitter, PersistedState);
|
||||
parentDelegationMixin(Events, PersistedState);
|
||||
|
||||
function PersistedState(value, path, parent) {
|
||||
function PersistedState(value, path, parent, silent) {
|
||||
PersistedState.Super.call(this);
|
||||
|
||||
this._path = this._setPath(path);
|
||||
|
@ -49,7 +64,7 @@ define(function (require) {
|
|||
value = value || this._getDefault();
|
||||
|
||||
// copy passed state values and create internal trackers
|
||||
this.set(value);
|
||||
(silent) ? this.setSilent(value) : this.set(value);
|
||||
this._initialized = true; // used to track state changes
|
||||
}
|
||||
|
||||
|
@ -58,31 +73,48 @@ define(function (require) {
|
|||
};
|
||||
|
||||
PersistedState.prototype.set = function (key, value) {
|
||||
// key must be the value, set the entire state using it
|
||||
if (_.isUndefined(value) && _.isPlainObject(key)) {
|
||||
// swap the key and value to write to the state
|
||||
value = key;
|
||||
key = undefined;
|
||||
}
|
||||
|
||||
// ensure the value being passed in is never mutated
|
||||
value = _.cloneDeep(value);
|
||||
|
||||
var val = this._set(key, value);
|
||||
var params = prepSetParams(key, value, this._path);
|
||||
var val = this._set(params.key, params.value);
|
||||
this.emit('set');
|
||||
return val;
|
||||
};
|
||||
|
||||
PersistedState.prototype.reset = function (key) {
|
||||
this.set(key, undefined);
|
||||
PersistedState.prototype.setSilent = function (key, value) {
|
||||
var params = prepSetParams(key, value, this._path);
|
||||
return this._set(params.key, params.value, true);
|
||||
};
|
||||
|
||||
PersistedState.prototype.clear = function (key) {
|
||||
this.set(key, null);
|
||||
PersistedState.prototype.reset = function (path) {
|
||||
var keyPath = this._getIndex(path);
|
||||
var origValue = _.get(this._defaultState, keyPath);
|
||||
var currentValue = _.get(this._mergedState, keyPath);
|
||||
|
||||
if (_.isUndefined(origValue)) {
|
||||
this._cleanPath(path, this._mergedState);
|
||||
} else {
|
||||
_.set(this._mergedState, keyPath, origValue);
|
||||
}
|
||||
|
||||
// clean up the changedState and defaultChildState trees
|
||||
this._cleanPath(path, this._changedState);
|
||||
this._cleanPath(path, this._defaultChildState);
|
||||
|
||||
if (!_.isEqual(currentValue, origValue)) this.emit('change');
|
||||
};
|
||||
|
||||
PersistedState.prototype.createChild = function (path, value) {
|
||||
return new PersistedState(value, this._getIndex(path), this._parent || this);
|
||||
PersistedState.prototype.createChild = function (path, value, silent) {
|
||||
this._setChild(this._getIndex(path), value, this._parent || this);
|
||||
return new PersistedState(value, this._getIndex(path), this._parent || this, silent);
|
||||
};
|
||||
|
||||
PersistedState.prototype.removeChild = function (path) {
|
||||
var origValue = _.get(this._defaultState, this._getIndex(path));
|
||||
|
||||
if (_.isUndefined(origValue)) {
|
||||
this.reset(path);
|
||||
} else {
|
||||
this.set(path, origValue);
|
||||
}
|
||||
};
|
||||
|
||||
PersistedState.prototype.getChanges = function () {
|
||||
|
@ -106,6 +138,29 @@ define(function (require) {
|
|||
return (this._path || []).concat(toPath(key));
|
||||
};
|
||||
|
||||
PersistedState.prototype._getPartialIndex = function (key) {
|
||||
var keyPath = this._getIndex(key);
|
||||
return keyPath.slice(this._path.length);
|
||||
};
|
||||
|
||||
PersistedState.prototype._cleanPath = function (path, stateTree) {
|
||||
var partialPath = this._getPartialIndex(path);
|
||||
var remove = true;
|
||||
|
||||
// recursively delete value tree, when no other keys exist
|
||||
while (partialPath.length > 0) {
|
||||
var lastKey = partialPath.splice(partialPath.length - 1, 1)[0];
|
||||
var statePath = this._path.concat(partialPath);
|
||||
var stateVal = statePath.length > 0 ? _.get(stateTree, statePath) : stateTree;
|
||||
|
||||
// if stateVal isn't an object, do nothing
|
||||
if (!_.isPlainObject(stateVal)) return;
|
||||
|
||||
if (remove) delete stateVal[lastKey];
|
||||
if (Object.keys(stateVal).length > 0) remove = false;
|
||||
}
|
||||
};
|
||||
|
||||
PersistedState.prototype._getDefault = function () {
|
||||
var def = (this._hasPath()) ? undefined : {};
|
||||
return (this._parent ? this.get() : def);
|
||||
|
@ -119,13 +174,18 @@ define(function (require) {
|
|||
return (isString) ? [this._getIndex(path)] : path;
|
||||
};
|
||||
|
||||
PersistedState.prototype._setChild = function (path, value, parent) {
|
||||
parent._defaultChildState = parent._defaultChildState || {};
|
||||
_.set(parent._defaultChildState, path, value);
|
||||
};
|
||||
|
||||
PersistedState.prototype._hasPath = function () {
|
||||
return this._path.length > 0;
|
||||
};
|
||||
|
||||
PersistedState.prototype._get = function (key, def) {
|
||||
// delegate to parent instance
|
||||
if (this._parent) return this._parent._get(this._getIndex(key), key);
|
||||
if (this._parent) return this._parent._get(this._getIndex(key), def);
|
||||
|
||||
// no path and no key, get the whole state
|
||||
if (!this._hasPath() && _.isUndefined(key)) {
|
||||
|
@ -135,11 +195,12 @@ define(function (require) {
|
|||
return _.get(this._mergedState, this._getIndex(key), def);
|
||||
};
|
||||
|
||||
PersistedState.prototype._set = function (key, value, initialChildState, defaultChildState) {
|
||||
PersistedState.prototype._set = function (key, value, silent, initialChildState) {
|
||||
var self = this;
|
||||
var stateChanged = false;
|
||||
var initialState = !this._initialized;
|
||||
var keyPath = this._getIndex(key);
|
||||
var hasKeyPath = keyPath.length > 0;
|
||||
|
||||
// if this is the initial state value, save value as the default
|
||||
if (initialState) {
|
||||
|
@ -150,43 +211,59 @@ define(function (require) {
|
|||
|
||||
// delegate to parent instance, passing child's default value
|
||||
if (this._parent) {
|
||||
return this._parent._set(keyPath, value, initialState, this._defaultState);
|
||||
return this._parent._set(keyPath, value, silent, initialState);
|
||||
}
|
||||
|
||||
// everything in here affects only the parent state
|
||||
if (!initialState) {
|
||||
// no path and no key, set the whole state
|
||||
if (!this._hasPath() && _.isUndefined(key)) {
|
||||
// check for changes and emit an event when found
|
||||
// compare changedState and new state, emit an event when different
|
||||
stateChanged = !_.isEqual(this._changedState, value);
|
||||
if (!initialChildState) this._changedState = value;
|
||||
} else {
|
||||
// check for changes and emit an event when found
|
||||
stateChanged = !_.isEqual(this.get(keyPath), value);
|
||||
|
||||
// arrays merge by index, not the desired behavior - ensure they are replaced
|
||||
if (!initialChildState) {
|
||||
this._changedState = value;
|
||||
this._mergedState = _.cloneDeep(value);
|
||||
}
|
||||
} else {
|
||||
// check for changes at path, emit an event when different
|
||||
var curVal = hasKeyPath ? this.get(keyPath) : this._mergedState;
|
||||
stateChanged = !_.isEqual(curVal, value);
|
||||
|
||||
if (!initialChildState) {
|
||||
// arrays are merge by index, not desired - ensure they are replaced
|
||||
if (_.isArray(_.get(this._mergedState, keyPath))) {
|
||||
_.set(this._mergedState, keyPath, undefined);
|
||||
if (hasKeyPath) _.set(this._mergedState, keyPath, undefined);
|
||||
else this._mergedState = undefined;
|
||||
}
|
||||
_.set(this._changedState, keyPath, value);
|
||||
|
||||
if (hasKeyPath) _.set(this._changedState, keyPath, value);
|
||||
else this._changedState = _.isPlainObject(value) ? value : {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the merged state value
|
||||
var targetObj = this._mergedState || _.cloneDeep(this._defaultState);
|
||||
var sourceObj = _.merge({}, defaultChildState, this._changedState);
|
||||
var sourceObj = _.merge({}, this._defaultChildState, this._changedState);
|
||||
|
||||
// handler arguments are (targetValue, sourceValue, key, target, source)
|
||||
var mergeMethod = function (targetValue, sourceValue, mergeKey) {
|
||||
// If `mergeMethod` is provided it is invoked to produce the merged values of the destination and
|
||||
// source properties. If `mergeMethod` returns `undefined` merging is handled by the method instead
|
||||
// handler arguments are (targetValue, sourceValue, key, target, source)
|
||||
// if not initial state, skip default merge method (ie. return value, see note below)
|
||||
if (!initialState && !initialChildState && _.isEqual(keyPath, self._getIndex(mergeKey))) {
|
||||
// use the sourceValue or fall back to targetValue
|
||||
return !_.isUndefined(sourceValue) ? sourceValue : targetValue;
|
||||
}
|
||||
};
|
||||
|
||||
// If `mergeMethod` is provided it is invoked to produce the merged values of the
|
||||
// destination and source properties.
|
||||
// If `mergeMethod` returns `undefined` the default merging method is used
|
||||
this._mergedState = _.merge(targetObj, sourceObj, mergeMethod);
|
||||
|
||||
if (stateChanged) this.emit('change');
|
||||
// sanity check; verify that there are actually changes
|
||||
if (_.isEqual(this._mergedState, this._defaultState)) this._changedState = {};
|
||||
|
||||
if (!silent && stateChanged) this.emit('change');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
|
|
@ -3,13 +3,17 @@ define(function (require) {
|
|||
var modules = require('ui/modules');
|
||||
var urlParam = '_a';
|
||||
|
||||
|
||||
function AppStateProvider(Private, $rootScope, getAppState) {
|
||||
var State = Private(require('ui/state_management/state'));
|
||||
|
||||
var PersistedState = Private(require('ui/persisted_state/persisted_state'));
|
||||
var persistedStates;
|
||||
var eventUnsubscribers;
|
||||
|
||||
_.class(AppState).inherits(State);
|
||||
function AppState(defaults) {
|
||||
persistedStates = {};
|
||||
eventUnsubscribers = [];
|
||||
|
||||
AppState.Super.call(this, urlParam, defaults);
|
||||
getAppState._set(this);
|
||||
}
|
||||
|
@ -17,10 +21,46 @@ define(function (require) {
|
|||
// if the url param is missing, write it back
|
||||
AppState.prototype._persistAcrossApps = false;
|
||||
|
||||
|
||||
AppState.prototype.destroy = function () {
|
||||
AppState.Super.prototype.destroy.call(this);
|
||||
getAppState._set(null);
|
||||
_.callEach(eventUnsubscribers);
|
||||
};
|
||||
|
||||
AppState.prototype.makeStateful = function (prop) {
|
||||
if (persistedStates[prop]) return persistedStates[prop];
|
||||
var self = this;
|
||||
|
||||
// set up the ui state
|
||||
persistedStates[prop] = new PersistedState();
|
||||
|
||||
// update the app state when the stateful instance changes
|
||||
var updateOnChange = function () {
|
||||
var replaceState = false; // TODO: debouncing logic
|
||||
|
||||
self[prop] = persistedStates[prop].getChanges();
|
||||
self.save(replaceState);
|
||||
};
|
||||
var handlerOnChange = (method) => persistedStates[prop][method]('change', updateOnChange);
|
||||
handlerOnChange('on');
|
||||
eventUnsubscribers.push(() => handlerOnChange('off'));
|
||||
|
||||
// update the stateful object when the app state changes
|
||||
var persistOnChange = function (changes) {
|
||||
if (!changes) return;
|
||||
|
||||
if (changes.indexOf(prop) !== -1) {
|
||||
persistedStates[prop].set(self[prop]);
|
||||
}
|
||||
};
|
||||
var handlePersist = (method) => this[method]('fetch_with_changes', persistOnChange);
|
||||
handlePersist('on');
|
||||
eventUnsubscribers.push(() => handlePersist('off'));
|
||||
|
||||
// if the thing we're making stateful has an appState value, write to persisted state
|
||||
if (self[prop]) persistedStates[prop].setSilent(self[prop]);
|
||||
|
||||
return persistedStates[prop];
|
||||
};
|
||||
|
||||
return AppState;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<div ng-click="toggleDisplay()" class="visualize-show-spy">
|
||||
<div class="visualize-show-spy-tab">
|
||||
<i class="fa" ng-class="spy.mode ? 'fa-chevron-down' : 'fa-chevron-up'"></i>
|
||||
<i class="fa" ng-class="spy.mode.name ? 'fa-chevron-down' : 'fa-chevron-up'"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="spy.mode" class="visualize-spy-container">
|
||||
<div ng-show="spy.mode.name" class="visualize-spy-container">
|
||||
<header class="control-group">
|
||||
<div class="fill visualize-spy-nav">
|
||||
<a
|
||||
class="btn btn-default"
|
||||
ng-repeat="mode in modes.inOrder"
|
||||
ng-class="{ active: spy.mode.name === mode.name }"
|
||||
ng-click="setSpyMode(mode)">
|
||||
ng-click="setSpyMode(mode.name)">
|
||||
{{mode.display}}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -5,57 +5,93 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var modes = Private(require('ui/registry/spy_modes'));
|
||||
var defaultMode = modes.inOrder[0];
|
||||
var spyModes = Private(require('ui/registry/spy_modes'));
|
||||
var defaultMode = spyModes.inOrder[0].name;
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('ui/visualize/spy.html'),
|
||||
link: function ($scope, $el) {
|
||||
var currentSpy;
|
||||
var $container = $el.find('.visualize-spy-container');
|
||||
var fullPageSpy = false;
|
||||
$scope.modes = modes;
|
||||
var fullPageSpy = _.get($scope.spy, 'mode.fill', false);
|
||||
$scope.modes = spyModes;
|
||||
|
||||
$scope.toggleDisplay = function () {
|
||||
$scope.setSpyMode($scope.spy.mode ? null : defaultMode);
|
||||
};
|
||||
function getSpyObject(name) {
|
||||
name = _.isUndefined(name) ? $scope.spy.mode.name : name;
|
||||
fullPageSpy = (_.isNull(name)) ? false : fullPageSpy;
|
||||
|
||||
$scope.toggleFullPage = function () {
|
||||
fullPageSpy = $scope.spy.mode.fill = !fullPageSpy;
|
||||
};
|
||||
return {
|
||||
name: name,
|
||||
fill: fullPageSpy,
|
||||
};
|
||||
}
|
||||
|
||||
$scope.setSpyMode = function (newMode) {
|
||||
// allow passing in a mode name
|
||||
if (_.isString(newMode)) newMode = modes.byName[newMode];
|
||||
|
||||
var current = $scope.spy.mode;
|
||||
var change = false;
|
||||
|
||||
// no change
|
||||
if (current && newMode && newMode.name === current.name) return;
|
||||
var renderSpy = function (spyName) {
|
||||
var newMode = $scope.modes.byName[spyName];
|
||||
|
||||
// clear the current value
|
||||
if (current) {
|
||||
current.$container.remove();
|
||||
current.$scope.$destroy();
|
||||
delete $scope.spy.mode;
|
||||
if (currentSpy) {
|
||||
currentSpy.$container && currentSpy.$container.remove();
|
||||
currentSpy.$scope && currentSpy.$scope.$destroy();
|
||||
$scope.spy.mode = {};
|
||||
currentSpy = null;
|
||||
}
|
||||
|
||||
// no further changes
|
||||
if (!newMode) return;
|
||||
|
||||
current = $scope.spy.mode = {
|
||||
// copy a couple values over
|
||||
name: newMode.name,
|
||||
display: newMode.display,
|
||||
fill: fullPageSpy,
|
||||
// update the spy mode and append to the container
|
||||
$scope.spy.mode = getSpyObject(newMode.name);
|
||||
|
||||
currentSpy = _.assign({
|
||||
$scope: $scope.$new(),
|
||||
$container: $('<div class="visualize-spy-content">').appendTo($container)
|
||||
};
|
||||
}, $scope.spy.mode);
|
||||
|
||||
current.$container.append($compile(newMode.template)(current.$scope));
|
||||
newMode.link && newMode.link(current.$scope, current.$container);
|
||||
currentSpy.$container.append($compile(newMode.template)(currentSpy.$scope));
|
||||
newMode.link && newMode.link(currentSpy.$scope, currentSpy.$container);
|
||||
};
|
||||
|
||||
$scope.toggleDisplay = function () {
|
||||
var modeName = _.get($scope.spy, 'mode.name');
|
||||
$scope.setSpyMode(modeName ? null : defaultMode);
|
||||
};
|
||||
|
||||
$scope.toggleFullPage = function () {
|
||||
fullPageSpy = !fullPageSpy;
|
||||
$scope.spy.mode = getSpyObject();
|
||||
};
|
||||
|
||||
$scope.setSpyMode = function (modeName) {
|
||||
// save the spy mode to the UI state
|
||||
if (!_.isString(modeName)) modeName = null;
|
||||
$scope.spy.mode = getSpyObject(modeName);
|
||||
};
|
||||
|
||||
if ($scope.uiState) {
|
||||
// sync external uiState changes
|
||||
var syncUIState = () => $scope.spy.mode = $scope.uiState.get('spy.mode');
|
||||
$scope.uiState.on('change', syncUIState);
|
||||
$scope.$on('$destroy', () => $scope.uiState.off('change', syncUIState));
|
||||
}
|
||||
|
||||
// re-render the spy when the name of fill modes change
|
||||
$scope.$watchMulti([
|
||||
'spy.mode.name',
|
||||
'spy.mode.fill'
|
||||
], function (newVals, oldVals) {
|
||||
// update the ui state, but only if it really changes
|
||||
var changedVals = newVals.filter((val) => !_.isUndefined(val)).length > 0;
|
||||
if (changedVals && !_.isEqual(newVals, oldVals)) {
|
||||
if ($scope.uiState) $scope.uiState.set('spy.mode', $scope.spy.mode);
|
||||
}
|
||||
|
||||
// ensure the fill mode is synced
|
||||
fullPageSpy = _.get($scope.spy, 'mode.fill', fullPageSpy);
|
||||
|
||||
renderSpy(_.get($scope.spy, 'mode.name', null));
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
<div class="item bottom"></div>
|
||||
</div>
|
||||
<div ng-hide="vis.type.requiresSearch && esResp.hits.total === 0"
|
||||
ng-style="loadingStyle"
|
||||
ng-class="{ loading: vis.type.requiresSearch && searchSource.activeFetchCount > 0 }"
|
||||
class="visualize-chart"></div>
|
||||
<!-- <pre>{{chartData | json}}</pre> -->
|
||||
<visualize-spy ng-if="vis.type.requiresSearch"></visualize-spy>
|
||||
<visualize-spy ng-if="vis.type.requiresSearch && showSpyPanel"></visualize-spy>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
define(function (require) {
|
||||
require('ui/modules')
|
||||
.get('kibana/directive')
|
||||
.directive('visualize', function (Notifier, SavedVis, indexPatterns, Private) {
|
||||
.directive('visualize', function (Notifier, SavedVis, indexPatterns, Private, config) {
|
||||
|
||||
require('ui/visualize/spy');
|
||||
require('ui/visualize/visualize.less');
|
||||
|
@ -16,16 +16,22 @@ define(function (require) {
|
|||
return {
|
||||
restrict: 'E',
|
||||
scope : {
|
||||
showSpyPanel: '=?',
|
||||
vis: '=',
|
||||
uiState: '=?',
|
||||
searchSource: '=?',
|
||||
editableVis: '=?',
|
||||
esResp: '=?',
|
||||
searchSource: '=?'
|
||||
},
|
||||
template: require('ui/visualize/visualize.html'),
|
||||
link: function ($scope, $el, attr) {
|
||||
var chart; // set in "vis" watcher
|
||||
var minVisChartHeight = 180;
|
||||
|
||||
if (_.isUndefined($scope.showSpyPanel)) {
|
||||
$scope.showSpyPanel = true;
|
||||
}
|
||||
|
||||
function getter(selector) {
|
||||
return function () {
|
||||
var $sel = $el.find(selector);
|
||||
|
@ -36,8 +42,9 @@ define(function (require) {
|
|||
var getVisEl = getter('.visualize-chart');
|
||||
var getSpyEl = getter('visualize-spy');
|
||||
|
||||
$scope.spy = {mode: false};
|
||||
$scope.fullScreenSpy = false;
|
||||
$scope.spy = {};
|
||||
$scope.spy.mode = ($scope.uiState) ? $scope.uiState.get('spy.mode', {}) : {};
|
||||
|
||||
var applyClassNames = function () {
|
||||
var $spyEl = getSpyEl();
|
||||
|
@ -76,7 +83,15 @@ define(function (require) {
|
|||
};
|
||||
}());
|
||||
|
||||
var loadingDelay = config.get('visualization:loadingDelay');
|
||||
$scope.loadingStyle = {
|
||||
'-webkit-transition-delay': loadingDelay,
|
||||
'transition-delay': loadingDelay
|
||||
};
|
||||
|
||||
// spy watchers
|
||||
$scope.$watch('fullScreenSpy', applyClassNames);
|
||||
|
||||
$scope.$watchCollection('spy.mode', function (spyMode, oldSpyMode) {
|
||||
var $visEl = getVisEl();
|
||||
if (!$visEl) return;
|
||||
|
@ -85,6 +100,7 @@ define(function (require) {
|
|||
if (spyMode && !oldSpyMode) {
|
||||
$scope.fullScreenSpy = $visEl.height() < minVisChartHeight;
|
||||
}
|
||||
|
||||
applyClassNames();
|
||||
});
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ visualize {
|
|||
.visualize-chart {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
-webkit-transition: opacity 0.01s;
|
||||
transition: opacity 0.01s;
|
||||
|
||||
&.spy-visible {
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = function (grunt) {
|
|||
|
||||
return {
|
||||
options: {
|
||||
branch: '2.0',
|
||||
branch: '2.1',
|
||||
fresh: !grunt.option('esvm-no-fresh'),
|
||||
config: {
|
||||
network: {
|
||||
|
|
Loading…
Reference in a new issue