mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Merge branch 'master' into fixed_scales
This commit is contained in:
commit
c7e8779d54
39 changed files with 478 additions and 145 deletions
|
@ -3,7 +3,7 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
var moment = require('moment');
|
||||
var BucketAggType = Private(require('components/agg_types/buckets/_bucket_agg_type'));
|
||||
var defaultPrecision = 3;
|
||||
var defaultPrecision = 2;
|
||||
|
||||
function getPrecision(precision) {
|
||||
var maxPrecision = _.parseInt(config.get('visualization:tileMap:maxPrecision'));
|
||||
|
@ -29,10 +29,23 @@ define(function (require) {
|
|||
name: 'field',
|
||||
filterFieldTypes: 'geo_point'
|
||||
},
|
||||
{
|
||||
name: 'autoPrecision',
|
||||
default: true,
|
||||
write: _.noop
|
||||
},
|
||||
{
|
||||
name: 'precision',
|
||||
default: defaultPrecision,
|
||||
editor: require('text!components/agg_types/controls/precision.html'),
|
||||
controller: function ($scope) {
|
||||
$scope.$watchMulti([
|
||||
'agg.params.autoPrecision',
|
||||
'outputAgg.params.precision'
|
||||
], function (cur, prev) {
|
||||
if (cur[1]) $scope.agg.params.precision = cur[1];
|
||||
});
|
||||
},
|
||||
deserialize: getPrecision,
|
||||
write: function (aggConfig, output) {
|
||||
output.params.precision = getPrecision(aggConfig.params.precision);
|
||||
|
@ -41,4 +54,4 @@ define(function (require) {
|
|||
]
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<div>
|
||||
<small><a target="_window" href="http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#date-math">Accepted Date Formats <i class="fa-link fa"></i></a></small>
|
||||
|
||||
<table class="vis-editor-agg-editor-ranges form-group">
|
||||
<table class="vis-editor-agg-editor-ranges form-group" ng-show="agg.params.ranges.length">
|
||||
<tr>
|
||||
<th>
|
||||
<label>From</label>
|
||||
|
@ -37,8 +35,23 @@
|
|||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<small>
|
||||
<a target="_window" href="http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#date-math">Accepted Date Formats <i class="fa-link fa"></i></a>
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input ng-model="agg.params.ranges.length" name="rangeLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.rangeLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one date range.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-click="agg.params.ranges.push({})"
|
||||
class="sidebar-item-button primary">
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
name="field"
|
||||
required
|
||||
ng-model="agg.params.field"
|
||||
ng-if="indexedFields.length"
|
||||
ng-show="indexedFields.length"
|
||||
auto-select-if-only-one="indexedFields"
|
||||
ng-options="field as field.displayName group by field.type for field in indexedFields"
|
||||
ng-change="aggParam.onChange(agg)">
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<input ng-model="agg.params.filters.length" name="filterLength" required min="1" type="number" class="ng-hide">
|
||||
<input ng-model="agg.params.filters.length" name="filterLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.filterLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one filter
|
||||
<strong>Required:</strong> You must specify at least one filter.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</p>
|
||||
|
||||
<div ng-show="agg.params.ipRangeType != 'mask'">
|
||||
<table class="vis-editor-agg-editor-ranges form-group">
|
||||
<table class="vis-editor-agg-editor-ranges form-group" ng-show="agg.params.ranges.fromTo.length">
|
||||
<tr>
|
||||
<th>
|
||||
<label>From</label>
|
||||
|
@ -43,6 +43,14 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<input ng-if="agg.params.ipRangeType != 'mask'" ng-model="agg.params.ranges.fromTo.length" name="rangeLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.rangeLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one IP range.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-click="agg.params.ranges.fromTo.push({})"
|
||||
class="sidebar-item-button primary">
|
||||
|
@ -51,7 +59,7 @@
|
|||
</div>
|
||||
|
||||
<div ng-show="agg.params.ipRangeType == 'mask'">
|
||||
<table class="vis-editor-agg-editor-ranges form-group">
|
||||
<table class="vis-editor-agg-editor-ranges form-group" ng-show="agg.params.ranges.mask.length">
|
||||
<tr>
|
||||
<th>
|
||||
<label>Mask</label>
|
||||
|
@ -79,6 +87,14 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<input ng-if="agg.params.ipRangeType == 'mask'" ng-model="agg.params.ranges.mask.length" name="rangeLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.rangeLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one IP range.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-click="agg.params.ranges.mask.push({})"
|
||||
class="sidebar-item-button primary">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="vis-editor-agg-form-row">
|
||||
<div class="form-group">
|
||||
<div class="vis-editor-agg-form-row" ng-controller="agg.type.params.byName.precision.controller">
|
||||
<div ng-if="!agg.params.autoPrecision" class="form-group">
|
||||
<label>Precision</label>
|
||||
<div class="vis-editor-agg-form-row">
|
||||
<input
|
||||
|
@ -16,4 +16,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vis-option-item">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="autoPrecision"
|
||||
ng-model="agg.params.autoPrecision">
|
||||
Change precision on map zoom
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<table class="vis-editor-agg-editor-ranges form-group">
|
||||
<table class="vis-editor-agg-editor-ranges form-group" ng-show="agg.params.ranges.length">
|
||||
<tr>
|
||||
<th>
|
||||
<label>From</label>
|
||||
|
@ -37,8 +37,16 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<input ng-model="agg.params.ranges.length" name="rangeLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.rangeLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one range.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-click="agg.params.ranges.push({})"
|
||||
class="sidebar-item-button primary">
|
||||
Add Range
|
||||
</div>
|
||||
</div>
|
|
@ -118,9 +118,10 @@ define(function (require) {
|
|||
};
|
||||
|
||||
// Listen for refreshInterval changes
|
||||
$rootScope.$watch('timefilter.refreshInterval', function () {
|
||||
$rootScope.$watchCollection('timefilter.refreshInterval', function () {
|
||||
var refreshValue = _.deepGet($rootScope, 'timefilter.refreshInterval.value');
|
||||
if (_.isNumber(refreshValue)) {
|
||||
var refreshPause = _.deepGet($rootScope, 'timefilter.refreshInterval.pause');
|
||||
if (_.isNumber(refreshValue) && !refreshPause) {
|
||||
self.fetchInterval(refreshValue);
|
||||
} else {
|
||||
self.fetchInterval(0);
|
||||
|
|
|
@ -9,6 +9,7 @@ doc-viewer .doc-viewer {
|
|||
}
|
||||
|
||||
&-value, pre {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<field-format-editor-numeral></field-format-editor-numeral>
|
|
@ -7,7 +7,6 @@ define(function (require) {
|
|||
return Numeral.factory({
|
||||
id: 'percent',
|
||||
title: 'Percentage',
|
||||
editorTemplate: require('text!components/stringify/editors/_numeral.html'),
|
||||
paramDefaults: new BoundToConfigObj({
|
||||
pattern: '=format:percent:defaultPattern',
|
||||
fractional: true
|
||||
|
|
21
src/kibana/components/timefilter/lib/diff_interval.js
Normal file
21
src/kibana/components/timefilter/lib/diff_interval.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function diffTimeProvider(Private) {
|
||||
var diff = Private(require('utils/diff_time_picker_vals'));
|
||||
|
||||
return function (self) {
|
||||
var oldRefreshInterval = _.clone(self.refreshInterval);
|
||||
|
||||
return function () {
|
||||
if (diff(self.refreshInterval, oldRefreshInterval)) {
|
||||
self.emit('update');
|
||||
if (!self.refreshInterval.pause && self.refreshInterval.value !== 0) {
|
||||
self.emit('fetch');
|
||||
}
|
||||
}
|
||||
|
||||
oldRefreshInterval = _.clone(self.refreshInterval);
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
17
src/kibana/components/timefilter/lib/diff_time.js
Normal file
17
src/kibana/components/timefilter/lib/diff_time.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function diffTimeProvider(Private) {
|
||||
var diff = Private(require('utils/diff_time_picker_vals'));
|
||||
|
||||
return function (self) {
|
||||
var oldTime = _.clone(self.time);
|
||||
return function () {
|
||||
if (diff(self.time, oldTime)) {
|
||||
self.emit('update');
|
||||
self.emit('fetch');
|
||||
}
|
||||
oldTime = _.clone(self.time);
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
define(function (require) {
|
||||
define(function (require) {
|
||||
require('modules')
|
||||
.get('kibana')
|
||||
.service('timefilter', function (Private, globalState, $rootScope) {
|
||||
|
@ -22,6 +22,8 @@ define(function (require) {
|
|||
Timefilter.Super.call(this);
|
||||
|
||||
var self = this;
|
||||
var diffTime = Private(require('components/timefilter/lib/diff_time'))(self);
|
||||
var diffInterval = Private(require('components/timefilter/lib/diff_interval'))(self);
|
||||
|
||||
self.enabled = false;
|
||||
|
||||
|
@ -32,6 +34,7 @@ define(function (require) {
|
|||
|
||||
var refreshIntervalDefaults = {
|
||||
display: 'Off',
|
||||
pause: false,
|
||||
section: 0,
|
||||
value: 0
|
||||
};
|
||||
|
@ -55,26 +58,19 @@ define(function (require) {
|
|||
});
|
||||
|
||||
$rootScope.$$timefilter = self;
|
||||
|
||||
$rootScope.$watchMulti([
|
||||
'$$timefilter.time',
|
||||
'$$timefilter.time.from',
|
||||
'$$timefilter.time.to',
|
||||
'$$timefilter.time.mode',
|
||||
'$$timefilter.time',
|
||||
'$$timefilter.time.mode'
|
||||
], diffTime);
|
||||
|
||||
$rootScope.$watchMulti([
|
||||
'$$timefilter.refreshInterval',
|
||||
'$$timefilter.refreshInterval.pause',
|
||||
'$$timefilter.refreshInterval.value'
|
||||
], (function () {
|
||||
var oldTime;
|
||||
var oldRefreshInterval;
|
||||
|
||||
return function () {
|
||||
if (diff(self.time, oldTime) || diff(self.refreshInterval, oldRefreshInterval)) {
|
||||
self.emit('update');
|
||||
}
|
||||
|
||||
oldTime = _.clone(self.time);
|
||||
oldRefreshInterval = _.clone(self.refreshInterval);
|
||||
};
|
||||
}()));
|
||||
], diffInterval);
|
||||
}
|
||||
|
||||
Timefilter.prototype.get = function (indexPattern) {
|
|
@ -19,30 +19,21 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist" ng-init="tab = 'filter'">
|
||||
<li ng-class="{active:tab== 'filter'}">
|
||||
<a href ng-click="tab = 'filter'">Time Filter</a>
|
||||
</li>
|
||||
<li ng-class="{active:tab== 'interval'}">
|
||||
<a href ng-click="tab = 'interval'">Refresh Interval</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- Filters -->
|
||||
<div ng-show="tab == 'filter'" role="tabpanel" class="tab-pane active">
|
||||
<div ng-show="activeTab === 'filter'" role="tabpanel" class="tab-pane active">
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked kbn-timepicker-modes">
|
||||
<li ng-class="{active: mode=='quick'}">
|
||||
<li ng-class="{active: mode === 'quick' }">
|
||||
<a ng-click="setMode('quick')">quick</a>
|
||||
</li>
|
||||
<li ng-class="{active: mode=='relative'}">
|
||||
<li ng-class="{active: mode === 'relative' }">
|
||||
<a ng-click="setMode('relative')">relative</a>
|
||||
</li>
|
||||
<li ng-class="{active: mode=='absolute'}">
|
||||
<li ng-class="{active: mode === 'absolute' }">
|
||||
<a ng-click="setMode('absolute')">absolute</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -167,12 +158,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Refresh Intervals -->
|
||||
<div ng-show="tab == 'interval'" role="tabpanel" class="tab-pane active">
|
||||
<div ng-show="activeTab === 'interval'" role="tabpanel" class="tab-pane active">
|
||||
<br>
|
||||
<div ng-repeat="list in refreshLists" class="kbn-refresh-section">
|
||||
<ul class="list-unstyled">
|
||||
<li ng-repeat="inter in list">
|
||||
<a class="refresh-interval" ng-class="{ 'refresh-interval-active': interval.value == inter.value }" ng-click="setRefreshInterval(inter)">
|
||||
<a class="refresh-interval" ng-class="{ 'refresh-interval-active': interval.value === inter.value }" ng-click="setRefreshInterval(inter)">
|
||||
{{inter.display}}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -18,7 +18,8 @@ define(function (require) {
|
|||
from: '=',
|
||||
to: '=',
|
||||
mode: '=',
|
||||
interval: '='
|
||||
interval: '=',
|
||||
activeTab: '='
|
||||
},
|
||||
template: html,
|
||||
controller: function ($scope) {
|
||||
|
@ -28,6 +29,8 @@ define(function (require) {
|
|||
|
||||
$scope.format = 'MMMM Do YYYY, HH:mm:ss.SSS';
|
||||
$scope.modes = ['quick', 'relative', 'absolute'];
|
||||
$scope.activeTab = $scope.activeTab || 'filter';
|
||||
|
||||
if (_.isUndefined($scope.mode)) $scope.mode = 'quick';
|
||||
|
||||
$scope.quickLists = _(quickRanges).groupBy('section').values().value();
|
||||
|
@ -137,6 +140,11 @@ define(function (require) {
|
|||
};
|
||||
|
||||
$scope.setRefreshInterval = function (interval) {
|
||||
interval = _.clone(interval);
|
||||
console.log('before: ' + interval.pause);
|
||||
interval.pause = (interval.pause == null || interval.pause === false) ? false : true;
|
||||
|
||||
console.log('after: ' + interval.pause);
|
||||
$scope.interval = interval;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function TileMapFactory(d3, Private) {
|
||||
return function TileMapFactory(d3, Private, config) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
var L = require('leaflet');
|
||||
|
@ -11,8 +11,8 @@ define(function (require) {
|
|||
|
||||
require('css!components/vislib/styles/main');
|
||||
|
||||
var mapCenter = [15, 5];
|
||||
var mapZoom = 2;
|
||||
var defaultMapCenter = [15, 5];
|
||||
var defaultMapZoom = 2;
|
||||
|
||||
/**
|
||||
* Tile Map Visualization: renders maps
|
||||
|
@ -62,14 +62,11 @@ define(function (require) {
|
|||
var worldBounds = L.latLngBounds([-90, -220], [90, 220]);
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function (data) {
|
||||
|
||||
if (self._attr.mapZoom) {
|
||||
mapZoom = self._attr.mapZoom;
|
||||
}
|
||||
if (self._attr.mapCenter) {
|
||||
mapCenter = self._attr.mapCenter;
|
||||
}
|
||||
self._attr.mapZoom = self._attr.mapZoom || defaultMapZoom;
|
||||
self._attr.mapCenter = self._attr.mapCenter || defaultMapCenter;
|
||||
|
||||
selection.each(function (data) {
|
||||
|
||||
var mapData = data.geoJson;
|
||||
var div = $(this).addClass('tilemap');
|
||||
|
@ -101,8 +98,8 @@ define(function (require) {
|
|||
minZoom: 1,
|
||||
maxZoom: 18,
|
||||
layers: tileLayer,
|
||||
center: mapCenter,
|
||||
zoom: mapZoom,
|
||||
center: self._attr.mapCenter,
|
||||
zoom: self._attr.mapZoom,
|
||||
noWrap: true,
|
||||
maxBounds: worldBounds,
|
||||
scrollWheelZoom: false,
|
||||
|
@ -126,8 +123,9 @@ define(function (require) {
|
|||
});
|
||||
|
||||
map.on('moveend', function setZoomCenter() {
|
||||
mapZoom = self._attr.mapZoom = map.getZoom();
|
||||
mapCenter = self._attr.mapCenter = map.getCenter();
|
||||
self._attr.mapZoom = map.getZoom();
|
||||
self._attr.mapCenter = map.getCenter();
|
||||
|
||||
featureLayer.clearLayers();
|
||||
featureLayer = self.markerType(map, mapData).addTo(map);
|
||||
});
|
||||
|
@ -155,14 +153,21 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
|
||||
map.on('zoomend', function () {
|
||||
self.events.emit('mapZoomEnd', {
|
||||
data: mapData,
|
||||
zoom: map.getZoom()
|
||||
});
|
||||
});
|
||||
|
||||
// add label for splits
|
||||
if (mapData.properties.label) {
|
||||
self.addLabel(mapData.properties.label, map);
|
||||
}
|
||||
|
||||
var fitContainer = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-control-zoom leaflet-control-fit');
|
||||
|
||||
if (mapData && mapData.features.length > 0) {
|
||||
var fitContainer = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-control-zoom leaflet-control-fit');
|
||||
|
||||
// Add button to fit container to points
|
||||
var FitControl = L.Control.extend({
|
||||
options: {
|
||||
|
@ -245,7 +250,6 @@ define(function (require) {
|
|||
* @return {Leaflet object} featureLayer
|
||||
*/
|
||||
TileMap.prototype.fitBounds = function (map, featureLayer) {
|
||||
|
||||
map.fitBounds(featureLayer.getBounds());
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ define(function (require) {
|
|||
ngModel.$formatters.unshift(validateCidrMask);
|
||||
|
||||
function validateCidrMask(mask) {
|
||||
if (mask == null || mask === '') {
|
||||
ngModel.$setValidity('cidrMaskInput', true);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
mask = new CidrMask(mask);
|
||||
ngModel.$setValidity('cidrMaskInput', true);
|
||||
|
|
|
@ -13,6 +13,11 @@ define(function (require) {
|
|||
},
|
||||
link: function ($scope, elem, attr, ngModel) {
|
||||
function validateIp(ipAddress) {
|
||||
if (ipAddress == null || ipAddress === '') {
|
||||
ngModel.$setValidity('ipInput', true);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ipAddress = new Ipv4Address(ipAddress);
|
||||
ngModel.$setValidity('ipInput', true);
|
||||
|
|
|
@ -94,7 +94,7 @@ define(function (require) {
|
|||
|
||||
timefilter.enabled = true;
|
||||
$scope.timefilter = timefilter;
|
||||
$scope.$listen(timefilter, 'update', $scope.refresh);
|
||||
$scope.$listen(timefilter, 'fetch', $scope.refresh);
|
||||
|
||||
courier.setRootSearchSource(dash.searchSource);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ define(function (require) {
|
|||
require('components/courier/courier');
|
||||
require('components/index_patterns/index_patterns');
|
||||
require('components/state_management/app_state');
|
||||
require('services/timefilter');
|
||||
require('components/timefilter/timefilter');
|
||||
require('components/highlight/highlight_tags');
|
||||
|
||||
var app = require('modules').get('apps/discover', [
|
||||
|
@ -148,7 +148,7 @@ define(function (require) {
|
|||
|
||||
$scope.updateDataSource()
|
||||
.then(function () {
|
||||
$scope.$listen(timefilter, 'update', function () {
|
||||
$scope.$listen(timefilter, 'fetch', function () {
|
||||
$scope.fetch();
|
||||
});
|
||||
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
from="timefilter.time.from"
|
||||
to="timefilter.time.to"
|
||||
mode="timefilter.time.mode"
|
||||
active-tab="timefilter.timepickerActiveTab"
|
||||
interval="timefilter.refreshInterval">
|
||||
</kbn-timepicker>
|
||||
</kbn-timepicker>
|
||||
|
|
|
@ -12,14 +12,21 @@ define(function (require) {
|
|||
});
|
||||
|
||||
var timepickerHtml = require('text!plugins/kibana/_timepicker.html');
|
||||
$scope.toggleTimepicker = function () {
|
||||
$scope.toggleTimepicker = function (tab) {
|
||||
tab = tab || timefilter.timepickerActiveTab || 'filter';
|
||||
|
||||
// Close if already open
|
||||
if ($scope.globalConfigTemplate === timepickerHtml) {
|
||||
if ($scope.globalConfigTemplate === timepickerHtml && timefilter.timepickerActiveTab === tab) {
|
||||
delete $scope.globalConfigTemplate;
|
||||
delete timefilter.timepickerActiveTab;
|
||||
} else {
|
||||
timefilter.timepickerActiveTab = tab;
|
||||
$scope.globalConfigTemplate = timepickerHtml;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleRefresh = function () {
|
||||
timefilter.refreshInterval.pause = !timefilter.refreshInterval.pause;
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,23 +22,36 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-timepicker">
|
||||
<li ng-show="httpActive.length" class="navbar-text hidden-xs">
|
||||
<div class="spinner"></div>
|
||||
<ul ng-if="setupComplete" ng-show="timefilter.enabled" class="nav navbar-nav navbar-right navbar-timepicker">
|
||||
<li>
|
||||
<a ng-click="toggleRefresh()"
|
||||
ng-show="timefilter.refreshInterval.value > 0">
|
||||
<i class="fa" ng-class="timefilter.refreshInterval.pause ? 'fa-play' : 'fa-pause'"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-if="setupComplete" ng-show="timefilter.enabled" class="navbar-timepicker-container">
|
||||
<a ng-click="toggleTimepicker()" aria-haspopup="true" aria-expanded="false">
|
||||
<span ng-show="timefilter.refreshInterval.value > 0" class="navbar-timepicker-auto-refresh-desc">
|
||||
{{timefilter.refreshInterval.display}}
|
||||
<i aria-hidden="true" class="fa fa-rotate-right"></i>
|
||||
</span>
|
||||
<span class="navbar-timepicker-timefilter-desc">
|
||||
<pretty-duration from="timefilter.time.from" to="timefilter.time.to"></pretty-duration>
|
||||
<i aria-hidden="true" class="fa fa-clock-o"></i>
|
||||
</span>
|
||||
<li ng-class="{active: timefilter.timepickerActiveTab === 'interval'}"
|
||||
ng-show="timefilter.refreshInterval.value > 0 || timefilter.timepickerActiveTab"
|
||||
class="to-body">
|
||||
<a ng-click="toggleTimepicker('interval')"
|
||||
class="navbar-timepicker-auto-refresh-desc">
|
||||
<span ng-show="timefilter.refreshInterval.value === 0"><i class="fa fa-repeat"></i> Auto-refresh</span>
|
||||
<span ng-show="timefilter.refreshInterval.value > 0">{{timefilter.refreshInterval.display}}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="to-body" ng-class="{active: timefilter.timepickerActiveTab === 'filter'}">
|
||||
<a ng-click="toggleTimepicker('filter')" aria-haspopup="true" aria-expanded="false" class="navbar-timepicker-time-desc">
|
||||
<i aria-hidden="true" class="fa fa-clock-o"></i>
|
||||
<pretty-duration from="timefilter.time.from" to="timefilter.time.to"></pretty-duration>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-timepicker" >
|
||||
<li ng-show="httpActive.length" class="navbar-text hidden-xs">
|
||||
<div class="spinner"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /Full navbar -->
|
||||
</div>
|
||||
|
@ -47,7 +60,8 @@
|
|||
<config
|
||||
ng-show="timefilter.enabled"
|
||||
config-template="globalConfigTemplate"
|
||||
config-object="timefilter">
|
||||
config-object="timefilter"
|
||||
config-close="toggleTimepicker">
|
||||
</config>
|
||||
|
||||
<div class="application" ng-view></div>
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
</div>
|
||||
|
||||
<div class="vis-option-item">
|
||||
</br>
|
||||
<label>
|
||||
<input type="checkbox" value="{{isDesaturated}}" ng-model="vis.params.isDesaturated" name="isDesaturated" ng-checked="vis.params.isDesaturated">
|
||||
<input type="checkbox"
|
||||
name="isDesaturated"
|
||||
ng-model="vis.params.isDesaturated">
|
||||
Desaturate map tiles
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function TileMapVisType(Private, getAppState) {
|
||||
return function TileMapVisType(Private, getAppState, courier, config) {
|
||||
var VislibVisType = Private(require('plugins/vis_types/vislib/_vislib_vis_type'));
|
||||
var Schemas = Private(require('plugins/vis_types/_schemas'));
|
||||
var geoJsonConverter = Private(require('components/agg_response/geo_json/geo_json'));
|
||||
|
@ -31,6 +31,38 @@ define(function (require) {
|
|||
filter.geo_bounding_box[field] = event.bounds;
|
||||
|
||||
pushFilter(filter, false, indexPatternName);
|
||||
},
|
||||
mapZoomEnd: function (event) {
|
||||
var agg = _.deepGet(event, 'data.properties.agg.geo');
|
||||
if (!agg || !agg.params.autoPrecision) return;
|
||||
|
||||
// zoomPrecision maps event.zoom to a geohash precision value
|
||||
// event.limit is the configurable max geohash precision
|
||||
// default max precision is 7, configurable up to 12
|
||||
var zoomPrecision = {
|
||||
1: 2,
|
||||
2: 2,
|
||||
3: 2,
|
||||
4: 3,
|
||||
5: 3,
|
||||
6: 4,
|
||||
7: 4,
|
||||
8: 5,
|
||||
9: 5,
|
||||
10: 6,
|
||||
11: 6,
|
||||
12: 7,
|
||||
13: 7,
|
||||
14: 8,
|
||||
15: 9,
|
||||
16: 10,
|
||||
17: 11,
|
||||
18: 12
|
||||
};
|
||||
|
||||
agg.params.precision = Math.min(zoomPrecision[event.zoom], config.get('visualization:tileMap:maxPrecision'));
|
||||
|
||||
courier.fetch();
|
||||
}
|
||||
},
|
||||
responseConverter: geoJsonConverter,
|
||||
|
|
|
@ -19,6 +19,7 @@ define(function (require) {
|
|||
template: require('text!plugins/visualize/editor/agg.html'),
|
||||
require: 'form',
|
||||
link: function ($scope, $el, attrs, kbnForm) {
|
||||
$scope.$bind('outputAgg', 'outputVis.aggs.byId[agg.id]', $scope);
|
||||
$scope.editorOpen = !!$scope.agg.brandNew;
|
||||
if (!$scope.editorOpen) {
|
||||
$scope.$evalAsync(kbnForm.$setTouched);
|
||||
|
|
|
@ -194,7 +194,7 @@ define(function (require) {
|
|||
// Without this manual emission, we'd miss filters and queries that were on the $state initially
|
||||
$state.emit('fetch_with_changes');
|
||||
|
||||
$scope.$listen(timefilter, 'update', _.bindKey($scope, 'fetch'));
|
||||
$scope.$listen(timefilter, 'fetch', _.bindKey($scope, 'fetch'));
|
||||
|
||||
$scope.$on('ready:vis', function () {
|
||||
$scope.$emit('application.load');
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<div class="sidebar-container">
|
||||
<form class="sidebar-list"
|
||||
ng-submit="visualizeEditor.$invalid ? dontApply() : stageEditableVis()"
|
||||
name="visualizeEditor">
|
||||
name="visualizeEditor"
|
||||
novalidate><!-- see http://goo.gl/9kgz5w -->
|
||||
|
||||
<div css-truncate title="{{indexPattern.id}}" ng-if="vis.type.requiresSearch" class="index-pattern">
|
||||
{{ indexPattern.id }}
|
||||
|
|
|
@ -14,6 +14,7 @@ define(function (require) {
|
|||
controllerAs: 'sidebar',
|
||||
controller: function ($scope) {
|
||||
$scope.$bind('vis', 'editableVis');
|
||||
$scope.$bind('outputVis', 'vis');
|
||||
this.section = _.get($scope, 'vis.type.requiresSearch') ? 'data' : 'options';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,4 +12,27 @@
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-active-arrow (@color: @body-bg) {
|
||||
|
||||
@media (max-width: @screen-sm-min) {
|
||||
&:before {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
position:absolute;
|
||||
border:@navbar-arrow-size solid @color;
|
||||
border-color:transparent transparent @color transparent;
|
||||
top:(@navbar-height - @navbar-arrow-size*2);
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
|
@ -258,12 +258,13 @@ notifications {
|
|||
}
|
||||
|
||||
.navbar-timepicker {
|
||||
&-auto-refresh-desc {
|
||||
margin-right: 20px;
|
||||
> li > a {
|
||||
padding-left: 7px !important;
|
||||
padding-right: 7px !important;
|
||||
}
|
||||
|
||||
&-timefilter-desc {
|
||||
|
||||
&-time-desc > .fa-clock-o {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.fa {
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
//@import url("//fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
||||
|
||||
@import (reference) "../_mixins.less";
|
||||
|
||||
|
||||
// Navbar =====================================================================
|
||||
|
||||
.navbar {
|
||||
|
@ -32,26 +35,7 @@
|
|||
}
|
||||
|
||||
.navbar-nav > .active > a {
|
||||
|
||||
@media (max-width: @screen-sm-min) {
|
||||
&:before {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
position:absolute;
|
||||
border:@navbar-arrow-size solid @body-bg;
|
||||
border-color:transparent transparent @body-bg transparent;
|
||||
top:(@navbar-height - @navbar-arrow-size*2);
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.nav-active-arrow(@body-bg);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,26 +57,14 @@
|
|||
color: lighten(@navbar-inverse-bg, 10%);
|
||||
}
|
||||
|
||||
.navbar-nav > .active > a {
|
||||
|
||||
@media (max-width: @screen-sm-min) {
|
||||
&:before {
|
||||
display: none !important;
|
||||
}
|
||||
.navbar-nav {
|
||||
> .active > a {
|
||||
.nav-active-arrow(@navbar-default-bg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
position:absolute;
|
||||
border:@navbar-arrow-size solid @navbar-default-bg;
|
||||
border-color:transparent transparent @navbar-default-bg transparent;
|
||||
top:(@navbar-height - @navbar-arrow-size*2);
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: 0;
|
||||
height: 0;
|
||||
> .active.to-body > a {
|
||||
.nav-active-arrow(@body-bg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ define(function (require) {
|
|||
valueOf(rangeA.to) !== valueOf(rangeB.to)
|
||||
|| valueOf(rangeA.from) !== valueOf(rangeB.from)
|
||||
|| valueOf(rangeA.value) !== valueOf(rangeB.value)
|
||||
|| valueOf(rangeA.pause) !== valueOf(rangeB.pause)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
75
test/unit/specs/components/timefilter/diff_interval.js
Normal file
75
test/unit/specs/components/timefilter/diff_interval.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
define(function (require) {
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
describe('Timefilter service', function () {
|
||||
describe('Refresh interval diff watcher', function () {
|
||||
|
||||
var fn, update, fetch, timefilter;
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(inject(function (Private) {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter = {
|
||||
refreshInterval: {
|
||||
pause: false,
|
||||
value: 0
|
||||
},
|
||||
emit: function (eventType) {
|
||||
if (eventType === 'update') update();
|
||||
if (eventType === 'fetch') fetch();
|
||||
}
|
||||
};
|
||||
|
||||
fn = Private(require('components/timefilter/lib/diff_interval'))(timefilter);
|
||||
}));
|
||||
|
||||
it('not emit anything if nothing has changed', function () {
|
||||
timefilter.refreshInterval = {pause: false, value: 0};
|
||||
fn();
|
||||
expect(update.called).to.be(false);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
it('emit only an update when paused', function () {
|
||||
timefilter.refreshInterval = {pause: true, value: 5000};
|
||||
fn();
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
it('emit update, not fetch, when switching to value: 0', function () {
|
||||
timefilter.refreshInterval = {pause: false, value: 5000};
|
||||
fn();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
timefilter.refreshInterval = {pause: false, value: 0};
|
||||
fn();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
it('should emit update, not fetch, when moving from unpaused to paused', function () {
|
||||
timefilter.refreshInterval = {pause: false, value: 5000};
|
||||
fn();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
timefilter.refreshInterval = {pause: true, value: 5000};
|
||||
fn();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledTwice).to.be(false);
|
||||
});
|
||||
|
||||
it('should emit update and fetch when unpaused', function () {
|
||||
timefilter.refreshInterval = {pause: true, value: 5000};
|
||||
fn();
|
||||
expect(update.calledOnce).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(false);
|
||||
timefilter.refreshInterval = {pause: false, value: 5000};
|
||||
fn();
|
||||
expect(update.calledTwice).to.be(true);
|
||||
expect(fetch.calledOnce).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
42
test/unit/specs/components/timefilter/diff_time.js
Normal file
42
test/unit/specs/components/timefilter/diff_time.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
define(function (require) {
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
describe('Timefilter service', function () {
|
||||
describe('time diff watcher', function () {
|
||||
|
||||
var fn, update, fetch, timefilter;
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(inject(function (Private) {
|
||||
update = sinon.spy();
|
||||
fetch = sinon.spy();
|
||||
timefilter = {
|
||||
time: {
|
||||
from: 0,
|
||||
to: 1
|
||||
},
|
||||
emit: function (eventType) {
|
||||
if (eventType === 'update') update();
|
||||
if (eventType === 'fetch') fetch();
|
||||
}
|
||||
};
|
||||
|
||||
fn = Private(require('components/timefilter/lib/diff_time'))(timefilter);
|
||||
}));
|
||||
|
||||
it('not emit anything if the time has not changed', function () {
|
||||
timefilter.time = {from: 0, to: 1};
|
||||
fn();
|
||||
expect(update.called).to.be(false);
|
||||
expect(fetch.called).to.be(false);
|
||||
});
|
||||
|
||||
it('emit update and fetch if the time has changed', function () {
|
||||
timefilter.time = {from: 5, to: 10};
|
||||
fn();
|
||||
expect(update.called).to.be(true);
|
||||
expect(fetch.called).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
|
@ -52,6 +52,7 @@ define(function (require) {
|
|||
' from="timefilter.time.from"' +
|
||||
' to="timefilter.time.to"' +
|
||||
' mode="timefilter.time.mode"' +
|
||||
' active-tab="timefilter.timepickerActiveTab"' +
|
||||
' interval="timefilter.refreshInterval">' +
|
||||
'</kbn-timepicker>'
|
||||
);
|
||||
|
@ -111,6 +112,35 @@ define(function (require) {
|
|||
done();
|
||||
});
|
||||
|
||||
it('should disable the looper when paused', function (done) {
|
||||
$scope.setRefreshInterval({ value : 1000, pause: true});
|
||||
$elem.scope().$digest();
|
||||
expect($courier.searchLooper.loopInterval()).to.be(0);
|
||||
expect($scope.interval.value).to.be(1000);
|
||||
done();
|
||||
});
|
||||
|
||||
it('but keep interval.value set', function (done) {
|
||||
$scope.setRefreshInterval({ value : 1000, pause: true});
|
||||
$elem.scope().$digest();
|
||||
expect($scope.interval.value).to.be(1000);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should unpause when setRefreshInterval is called without pause:true', function (done) {
|
||||
$scope.setRefreshInterval({ value : 1000, pause: true});
|
||||
expect($scope.interval.pause).to.be(true);
|
||||
|
||||
$scope.setRefreshInterval({ value : 1000, pause: false});
|
||||
expect($scope.interval.pause).to.be(false);
|
||||
|
||||
$scope.setRefreshInterval({ value : 1000});
|
||||
expect($scope.interval.pause).to.be(false);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('should highlight the current active interval', function (done) {
|
||||
$scope.setRefreshInterval({ value: 300000 });
|
||||
$elem.scope().$digest();
|
||||
|
|
|
@ -13,6 +13,22 @@ define(function (require) {
|
|||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('should allow empty input', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
$rootScope.value = '';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
|
||||
$rootScope.value = null;
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
|
||||
$rootScope.value = undefined;
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
});
|
||||
|
||||
it('should allow valid CIDR masks', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
|
@ -36,10 +52,6 @@ define(function (require) {
|
|||
it('should disallow invalid CIDR masks', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
$rootScope.value = '';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
|
||||
$rootScope.value = 'hello, world';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
|
|
|
@ -13,6 +13,22 @@ define(function (require) {
|
|||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('should allow empty input', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
$rootScope.value = '';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
|
||||
$rootScope.value = null;
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
|
||||
$rootScope.value = undefined;
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-valid')).to.be.ok();
|
||||
});
|
||||
|
||||
it('should allow valid IP addresses', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
|
@ -36,10 +52,6 @@ define(function (require) {
|
|||
it('should disallow invalid IP addresses', function () {
|
||||
var element = $compile(html)($rootScope);
|
||||
|
||||
$rootScope.value = '';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
|
||||
$rootScope.value = 'hello, world';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
|
@ -56,7 +68,7 @@ define(function (require) {
|
|||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
|
||||
$rootScope.value = Number.MAX_SAFE_INTEGER;
|
||||
$rootScope.value = Number.MAX_VALUE;
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-invalid')).to.be.ok();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue