mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Merge branch 'master' into tilemap-heatmap
This commit is contained in:
commit
69c1e2803c
31 changed files with 616 additions and 336 deletions
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label>Values</label>
|
||||
<kbn-number-list
|
||||
ng-model="agg.params.values"
|
||||
unit-name="value"
|
||||
range="[-Infinity,Infinity]"
|
||||
>
|
||||
</kbn-number-list>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label>Percents</label>
|
||||
<kbn-number-list
|
||||
ng-model="agg.params.percents"
|
||||
unit-name="percent"
|
||||
range="[0,100]"
|
||||
>
|
||||
</kbn-number-list>
|
||||
</div>
|
|
@ -1,34 +0,0 @@
|
|||
<div class="form-group" ng-controller="agg.type.params.byName.percents.controller">
|
||||
<label>Percentiles</label>
|
||||
<div
|
||||
ng-repeat="value in agg.params.percents track by $index"
|
||||
class="form-group vis-editor-agg-form-row vis-editor-agg-form-row">
|
||||
|
||||
<input
|
||||
ng-model="agg.params.percents[$index]"
|
||||
values-list="agg.params.percents"
|
||||
values-list-min="0"
|
||||
values-list-max="100"
|
||||
input-focus
|
||||
class="form-control">
|
||||
|
||||
<button type="button" ng-click="remove($index, 1)" class="btn btn-danger btn-xs">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input ng-model="validLength" name="validLength" required type="hidden">
|
||||
<div class="hintbox" ng-show="aggForm.validLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You mush specify at least one percentile
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
ng-click="add()"
|
||||
type="button"
|
||||
class="sidebar-item-button primary">
|
||||
<i class="fa fa-plus"></i> Add Percent
|
||||
</button>
|
||||
</div>
|
|
@ -1,33 +0,0 @@
|
|||
<div class="form-group" ng-controller="agg.type.params.byName.values.controller">
|
||||
<label>Values</label>
|
||||
<div
|
||||
ng-repeat="value in agg.params.values track by $index"
|
||||
class="form-group vis-editor-agg-form-row vis-editor-agg-form-row">
|
||||
|
||||
<input
|
||||
ng-model="agg.params.values[$index]"
|
||||
values-list="agg.params.values"
|
||||
values-list-min="0"
|
||||
input-focus
|
||||
class="form-control">
|
||||
|
||||
<button type="button" ng-click="remove($index, 1)" class="btn btn-danger btn-xs">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input ng-model="validLength" name="validLength" required type="hidden">
|
||||
<div class="hintbox" ng-show="aggForm.validLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Required:</strong> You must specify at least one value
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
ng-click="add()"
|
||||
type="button"
|
||||
class="sidebar-item-button primary">
|
||||
<i class="fa fa-plus"></i> Add value
|
||||
</button>
|
||||
</div>
|
|
@ -5,8 +5,9 @@ define(function (require) {
|
|||
var MetricAggType = Private(require('components/agg_types/metrics/_metric_agg_type'));
|
||||
var getResponseAggConfig = Private(require('components/agg_types/metrics/_get_response_agg_config'));
|
||||
|
||||
require('components/agg_types/controls/_values_list');
|
||||
var valuesEditor = require('text!components/agg_types/controls/values.html');
|
||||
var valuesEditor = require('text!components/agg_types/controls/percentile_ranks.html');
|
||||
// required by the values editor
|
||||
require('components/number_list/number_list');
|
||||
|
||||
var valueProps = {
|
||||
makeLabel: function () {
|
||||
|
@ -28,20 +29,7 @@ define(function (require) {
|
|||
{
|
||||
name: 'values',
|
||||
editor: valuesEditor,
|
||||
default: [],
|
||||
controller: function ($scope) {
|
||||
$scope.remove = function (index) {
|
||||
$scope.agg.params.values.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.add = function () {
|
||||
$scope.agg.params.values.push(_.last($scope.agg.params.values) + 1);
|
||||
};
|
||||
|
||||
$scope.$watchCollection('agg.params.values', function (values) {
|
||||
$scope.validLength = _.size(values) || null;
|
||||
});
|
||||
}
|
||||
default: []
|
||||
}
|
||||
],
|
||||
getResponseAggs: function (agg) {
|
||||
|
@ -60,4 +48,4 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,8 +6,9 @@ define(function (require) {
|
|||
var getResponseAggConfig = Private(require('components/agg_types/metrics/_get_response_agg_config'));
|
||||
var ordinalSuffix = require('utils/ordinal_suffix');
|
||||
|
||||
require('components/agg_types/controls/_values_list');
|
||||
var percentEditor = require('text!components/agg_types/controls/percents.html');
|
||||
var percentsEditor = require('text!components/agg_types/controls/percentiles.html');
|
||||
// required by the percentiles editor
|
||||
require('components/number_list/number_list');
|
||||
|
||||
var valueProps = {
|
||||
makeLabel: function () {
|
||||
|
@ -28,21 +29,8 @@ define(function (require) {
|
|||
},
|
||||
{
|
||||
name: 'percents',
|
||||
editor: percentEditor,
|
||||
default: [1, 5, 25, 50, 75, 95, 99],
|
||||
controller: function ($scope) {
|
||||
$scope.remove = function (index) {
|
||||
$scope.agg.params.percents.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.add = function () {
|
||||
$scope.agg.params.percents.push(_.last($scope.agg.params.percents) + 1);
|
||||
};
|
||||
|
||||
$scope.$watchCollection('agg.params.percents', function (percents) {
|
||||
$scope.validLength = _.size(percents) || null;
|
||||
});
|
||||
}
|
||||
editor: percentsEditor,
|
||||
default: [1, 5, 25, 50, 75, 95, 99]
|
||||
}
|
||||
],
|
||||
getResponseAggs: function (agg) {
|
||||
|
@ -61,4 +49,4 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -43,6 +43,7 @@ define(function (require) {
|
|||
|
||||
// the id of the document
|
||||
self.id = config.id || void 0;
|
||||
self.defaults = config.defaults;
|
||||
|
||||
/**
|
||||
* Asynchronously initialize this object - will only run
|
||||
|
@ -185,14 +186,12 @@ define(function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save this object
|
||||
* Serialize this object
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolved {String} - The id of the doc
|
||||
* @return {Object}
|
||||
*/
|
||||
self.save = function () {
|
||||
self.serialize = function () {
|
||||
var body = {};
|
||||
|
||||
_.forOwn(mapping, function (fieldMapping, fieldName) {
|
||||
|
@ -209,6 +208,18 @@ define(function (require) {
|
|||
};
|
||||
}
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save this object
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolved {String} - The id of the doc
|
||||
*/
|
||||
self.save = function () {
|
||||
|
||||
var body = self.serialize();
|
||||
|
||||
// Slugify the object id
|
||||
self.id = slugifyId(self.id);
|
||||
|
|
34
src/kibana/components/number_list/number_list.html
Normal file
34
src/kibana/components/number_list/number_list.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div
|
||||
ng-repeat="value in numberListCntr.getList() track by $index"
|
||||
class="form-group vis-editor-agg-form-row vis-editor-agg-form-row">
|
||||
|
||||
<input
|
||||
ng-model="numberListCntr.getList()[$index]"
|
||||
kbn-number-list-input
|
||||
input-focus
|
||||
class="form-control">
|
||||
|
||||
<button
|
||||
ng-click="numberListCntr.remove($index, 1)"
|
||||
class="btn btn-danger btn-xs"
|
||||
type="button">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<p ng-show="numberListCntr.invalidLength()" class="text-danger text-center">
|
||||
You must specify at least one {{numberListCntr.getUnitName()}}
|
||||
</p>
|
||||
|
||||
<p ng-show="numberListCntr.undefinedLength()" class="text-primary text-center">
|
||||
<!-- be a bit more polite when the form is first init'd -->
|
||||
Please specify at least one {{numberListCntr.getUnitName()}}
|
||||
</p>
|
||||
|
||||
<button
|
||||
ng-click="numberListCntr.add()"
|
||||
type="button"
|
||||
class="sidebar-item-button primary">
|
||||
<i class="fa fa-plus"></i> Add {{numberListCntr.getUnitName()}}
|
||||
</button>
|
108
src/kibana/components/number_list/number_list.js
Normal file
108
src/kibana/components/number_list/number_list.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var parseRange = require('utils/range');
|
||||
|
||||
require('components/number_list/number_list_input');
|
||||
require('modules')
|
||||
.get('kibana')
|
||||
.directive('kbnNumberList', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('text!components/number_list/number_list.html'),
|
||||
controllerAs: 'numberListCntr',
|
||||
require: 'ngModel',
|
||||
controller: function ($scope, $attrs, $parse) {
|
||||
var self = this;
|
||||
|
||||
// Called from the pre-link function once we have the controllers
|
||||
self.init = function (modelCntr) {
|
||||
self.modelCntr = modelCntr;
|
||||
|
||||
self.getList = function () {
|
||||
return self.modelCntr.$modelValue;
|
||||
};
|
||||
|
||||
self.getUnitName = _.partial($parse($attrs.unit), $scope);
|
||||
|
||||
var defaultRange = self.range = parseRange('[0,Infinity)');
|
||||
|
||||
$scope.$watch(function () {
|
||||
return $attrs.range;
|
||||
}, function (range, prev) {
|
||||
if (!range) {
|
||||
self.range = defaultRange;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
self.range = parseRange(range);
|
||||
} catch (e) {
|
||||
throw new TypeError('Unable to parse range: ' + e.message);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Remove an item from list by index
|
||||
* @param {number} index
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.remove = function (index) {
|
||||
var list = self.getList();
|
||||
if (!list) return;
|
||||
|
||||
list.splice(index, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an item to the end of the list
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.add = function () {
|
||||
var list = self.getList();
|
||||
if (!list) return;
|
||||
|
||||
list.push(_.last(list) + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if the list is too short.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
self.tooShort = function () {
|
||||
return _.size(self.getList()) < 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if the list is too short, but simply
|
||||
* because the user hasn't interacted with it yet
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
self.undefinedLength = function () {
|
||||
return self.tooShort() && (self.modelCntr.$untouched && self.modelCntr.$pristine);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if the list is too short
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
self.invalidLength = function () {
|
||||
return self.tooShort() && !self.undefinedLength();
|
||||
};
|
||||
|
||||
$scope.$watchCollection(self.getList, function () {
|
||||
self.modelCntr.$setValidity('numberListLength', !self.tooShort());
|
||||
});
|
||||
};
|
||||
},
|
||||
link: {
|
||||
pre: function ($scope, $el, attrs, ngModelCntr) {
|
||||
$scope.numberListCntr.init(ngModelCntr);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
});
|
|
@ -6,18 +6,21 @@ define(function (require) {
|
|||
var INVALID = {}; // invalid flag
|
||||
var FLOATABLE = /^[\d\.e\-\+]+$/i;
|
||||
|
||||
var VALIDATION_ERROR = 'numberListRangeAndOrder';
|
||||
var DIRECTIVE_ATTR = 'kbn-number-list-input';
|
||||
|
||||
require('modules')
|
||||
.get('kibana')
|
||||
.directive('valuesList', function ($parse) {
|
||||
.directive('kbnNumberListInput', function ($parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function ($scope, $el, attrs, ngModelController) {
|
||||
require: ['ngModel', '^kbnNumberList'],
|
||||
link: function ($scope, $el, attrs, controllers) {
|
||||
var ngModelCntr = controllers[0];
|
||||
var numberListCntr = controllers[1];
|
||||
|
||||
var $setModel = $parse(attrs.ngModel).assign;
|
||||
var $repeater = $el.closest('[ng-repeat]');
|
||||
var $listGetter = $parse(attrs.valuesList);
|
||||
var $minValue = $parse(attrs.valuesListMin);
|
||||
var $maxValue = $parse(attrs.valuesListMax);
|
||||
|
||||
var handlers = {
|
||||
up: change(add, 1),
|
||||
|
@ -29,14 +32,16 @@ define(function (require) {
|
|||
tab: go('next'),
|
||||
'shift-tab': go('prev'),
|
||||
|
||||
'shift-enter': numberListCntr.add,
|
||||
|
||||
backspace: removeIfEmpty,
|
||||
delete: removeIfEmpty
|
||||
};
|
||||
|
||||
function removeIfEmpty(event) {
|
||||
if ($el.val() === '') {
|
||||
if (!ngModelCntr.$viewValue) {
|
||||
$get('prev').focus();
|
||||
$scope.remove($scope.$index);
|
||||
numberListCntr.remove($scope.$index);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
@ -44,7 +49,7 @@ define(function (require) {
|
|||
}
|
||||
|
||||
function $get(dir) {
|
||||
return $repeater[dir]().find('[values-list]');
|
||||
return $repeater[dir]().find('[' + DIRECTIVE_ATTR + ']');
|
||||
}
|
||||
|
||||
function go(dir) {
|
||||
|
@ -88,7 +93,7 @@ define(function (require) {
|
|||
|
||||
function change(using, mod) {
|
||||
return function () {
|
||||
var str = String(ngModelController.$viewValue);
|
||||
var str = String(ngModelCntr.$viewValue);
|
||||
var val = parse(str);
|
||||
if (val === INVALID) return;
|
||||
|
||||
|
@ -96,7 +101,7 @@ define(function (require) {
|
|||
if (next === INVALID) return;
|
||||
|
||||
$el.val(next);
|
||||
ngModelController.$setViewValue(next);
|
||||
ngModelCntr.$setViewValue(next);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -117,17 +122,26 @@ define(function (require) {
|
|||
});
|
||||
|
||||
function parse(viewValue) {
|
||||
viewValue = String(viewValue || 0);
|
||||
var num = viewValue.trim();
|
||||
if (!FLOATABLE.test(num)) return INVALID;
|
||||
num = parseFloat(num);
|
||||
if (isNaN(num)) return INVALID;
|
||||
var num = viewValue;
|
||||
|
||||
var list = $listGetter($scope);
|
||||
var min = list[$scope.$index - 1] || $minValue($scope);
|
||||
var max = list[$scope.$index + 1] || $maxValue($scope);
|
||||
if (typeof num !== 'number' || isNaN(num)) {
|
||||
// parse non-numbers
|
||||
num = String(viewValue || 0).trim();
|
||||
if (!FLOATABLE.test(num)) return INVALID;
|
||||
|
||||
if (num <= min || num >= max) return INVALID;
|
||||
num = parseFloat(num);
|
||||
if (isNaN(num)) return INVALID;
|
||||
}
|
||||
|
||||
var range = numberListCntr.range;
|
||||
if (!range.within(num)) return INVALID;
|
||||
|
||||
if ($scope.$index > 0) {
|
||||
var i = $scope.$index - 1;
|
||||
var list = numberListCntr.getList();
|
||||
var prev = list[i];
|
||||
if (num <= prev) return INVALID;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
@ -137,31 +151,35 @@ define(function (require) {
|
|||
{
|
||||
fn: $scope.$watchCollection,
|
||||
get: function () {
|
||||
return $listGetter($scope);
|
||||
return numberListCntr.getList();
|
||||
}
|
||||
}
|
||||
], function () {
|
||||
var valid = parse(ngModelController.$viewValue) !== INVALID;
|
||||
ngModelController.$setValidity('valuesList', valid);
|
||||
var valid = parse(ngModelCntr.$viewValue) !== INVALID;
|
||||
ngModelCntr.$setValidity(VALIDATION_ERROR, valid);
|
||||
});
|
||||
|
||||
function validate(then) {
|
||||
return function (input) {
|
||||
var value = parse(input);
|
||||
var valid = value !== INVALID;
|
||||
value = valid ? value : void 0;
|
||||
ngModelController.$setValidity('valuesList', valid);
|
||||
value = valid ? value : input;
|
||||
ngModelCntr.$setValidity(VALIDATION_ERROR, valid);
|
||||
then && then(input, value);
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
ngModelController.$parsers.push(validate());
|
||||
ngModelController.$formatters.push(validate(function (input, value) {
|
||||
ngModelCntr.$parsers.push(validate());
|
||||
ngModelCntr.$formatters.push(validate(function (input, value) {
|
||||
if (input !== value) $setModel($scope, value);
|
||||
}));
|
||||
|
||||
if (parse(ngModelCntr.$viewValue) === INVALID) {
|
||||
ngModelCntr.$setTouched();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -1,19 +1,23 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var modules = require('modules');
|
||||
var urlParam = '_a';
|
||||
|
||||
|
||||
function AppStateProvider(Private, $rootScope, getAppState) {
|
||||
var State = Private(require('components/state_management/state'));
|
||||
|
||||
|
||||
_(AppState).inherits(State);
|
||||
function AppState(defaults) {
|
||||
AppState.Super.call(this, '_a', defaults);
|
||||
AppState.Super.call(this, urlParam, defaults);
|
||||
getAppState._set(this);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -26,13 +30,19 @@ define(function (require) {
|
|||
.factory('AppState', function (Private) {
|
||||
return Private(AppStateProvider);
|
||||
})
|
||||
.service('getAppState', function () {
|
||||
.service('getAppState', function ($location) {
|
||||
var currentAppState;
|
||||
|
||||
function get() {
|
||||
return currentAppState;
|
||||
}
|
||||
|
||||
// Checks to see if the appState might already exist, even if it hasn't been newed up
|
||||
get.previouslyStored = function () {
|
||||
var search = $location.search();
|
||||
return search[urlParam] ? true : false;
|
||||
};
|
||||
|
||||
get._set = function (current) {
|
||||
currentAppState = current;
|
||||
};
|
||||
|
|
|
@ -133,7 +133,7 @@ define(function (require) {
|
|||
var events = self.events ? self.events.eventResponse(d, i) : d;
|
||||
return render(tooltipFormatter(events));
|
||||
})
|
||||
.on('mouseout.tip', function () {
|
||||
.on('mouseleave.tip', function () {
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -358,7 +358,7 @@ define(function (require) {
|
|||
} else if (this._attr.mapType === 'Heatmap') {
|
||||
features = this.heatMap(map, mapData);
|
||||
} else {
|
||||
features = this.pinMarkers(mapData);
|
||||
features = this.scaledCircleMarkers(map, mapData);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ define(function (require) {
|
|||
|
||||
app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, kbnUrl) {
|
||||
return {
|
||||
controller: function ($scope, $route, $routeParams, $location, configFile, Private) {
|
||||
controller: function ($scope, $route, $routeParams, $location, configFile, Private, getAppState) {
|
||||
var filterBarWatchFilters = Private(require('components/filter_bar/lib/watchFilters'));
|
||||
|
||||
var notify = new Notifier({
|
||||
|
@ -57,6 +57,12 @@ define(function (require) {
|
|||
});
|
||||
|
||||
var dash = $scope.dash = $route.current.locals.dash;
|
||||
|
||||
if (dash.timeRestore && dash.timeTo && dash.timeFrom && !getAppState.previouslyStored()) {
|
||||
timefilter.time.to = dash.timeTo;
|
||||
timefilter.time.from = dash.timeFrom;
|
||||
}
|
||||
|
||||
$scope.$on('$destroy', dash.destroy);
|
||||
|
||||
var matchQueryFilter = function (filter) {
|
||||
|
@ -135,6 +141,8 @@ define(function (require) {
|
|||
$state.title = dash.id = dash.title;
|
||||
$state.save();
|
||||
dash.panelsJSON = angular.toJson($state.panels);
|
||||
dash.timeFrom = dash.timeRestore ? timefilter.time.from : undefined;
|
||||
dash.timeTo = dash.timeRestore ? timefilter.time.to : undefined;
|
||||
|
||||
dash.save()
|
||||
.then(function (id) {
|
||||
|
|
|
@ -3,5 +3,11 @@
|
|||
<label for="dashboardTitle">Save As</label>
|
||||
<input id="dashboardTitle" type="text" ng-model="opts.dashboard.title" class="form-control" placeholder="Dashboard title" input-focus="select">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="opts.dashboard.timeRestore" ng-checked="opts.dashboard.timeRestore">
|
||||
Store time with dashboard <i class="fa fa-info-circle ng-scope" tooltip="Change the time filter to the currently selected time each time this dashboard is loaded" tooltip-placement="" tooltip-popup-delay="250"></i>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" ng-disabled="!opts.dashboard.title" class="btn btn-primary" aria-label="Save dashboard">Save</button>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('app/dashboard');
|
||||
var _ = require('lodash');
|
||||
var moment = require('moment');
|
||||
|
||||
// Used only by the savedDashboards service, usually no reason to change this
|
||||
module.factory('SavedDashboard', function (courier) {
|
||||
|
@ -23,7 +24,10 @@ define(function (require) {
|
|||
hits: 'integer',
|
||||
description: 'string',
|
||||
panelsJSON: 'string',
|
||||
version: 'integer'
|
||||
version: 'integer',
|
||||
timeRestore: 'boolean',
|
||||
timeTo: 'string',
|
||||
timeFrom: 'string'
|
||||
},
|
||||
|
||||
// defeult values to assign to the doc
|
||||
|
@ -32,7 +36,10 @@ define(function (require) {
|
|||
hits: 0,
|
||||
description: '',
|
||||
panelsJSON: '[]',
|
||||
version: 1
|
||||
version: 1,
|
||||
timeRestore: false,
|
||||
timeTo: undefined,
|
||||
timeFrom: undefined
|
||||
},
|
||||
|
||||
searchSource: true,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<textarea rows="1" msd-elastic ng-if="field.type === 'text'" ng-model="field.value" class="form-control span12"/>
|
||||
<input ng-if="field.type === 'number'" type="number" ng-model="field.value" class="form-control span12"/>
|
||||
<div ng-if="field.type === 'json' || field.type === 'array'" ui-ace="{ onLoad: aceLoaded, mode: 'json' }" id="{{field.name}}" ng-model="field.value" class="form-control"></div>
|
||||
<input ng-if="field.type === 'boolean'" type="checkbox" ng-model="field.value" ng-checked="field.value">
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group">
|
||||
|
|
|
@ -55,11 +55,12 @@ define(function (require) {
|
|||
} else if (_.isArray(field.value)) {
|
||||
field.type = 'array';
|
||||
field.value = angular.toJson(field.value, true);
|
||||
} else if (_.isBoolean(field.value)) {
|
||||
field.type = 'boolean';
|
||||
field.value = field.value;
|
||||
} else if (_.isPlainObject(field.value)) {
|
||||
// do something recursive
|
||||
return _.reduce(field.value, _.partialRight(createField, parents), memo);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
memo.push(field);
|
||||
|
@ -74,15 +75,10 @@ define(function (require) {
|
|||
|
||||
$scope.title = inflection.singularize(serviceObj.title);
|
||||
|
||||
es.get({
|
||||
index: config.file.kibana_index,
|
||||
type: service.type,
|
||||
id: $routeParams.id
|
||||
})
|
||||
.then(function (obj) {
|
||||
service.get($routeParams.id).then(function (obj) {
|
||||
$scope.obj = obj;
|
||||
$scope.link = service.urlFor(obj._id);
|
||||
$scope.fields = _.reduce(obj._source, createField, []);
|
||||
$scope.link = service.urlFor(obj.id);
|
||||
$scope.fields = _.reduce(_.defaults(obj.serialize(), obj.defaults), createField, []);
|
||||
})
|
||||
.catch(notify.fatal);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ define(function (require) {
|
|||
isDesaturated: true,
|
||||
addLeafletPopup: true
|
||||
},
|
||||
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap', 'Pins'],
|
||||
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap'],
|
||||
editor: require('text!plugins/vis_types/vislib/editors/tile_map.html')
|
||||
},
|
||||
responseConverter: geoJsonConverter,
|
||||
|
|
|
@ -1,84 +1,81 @@
|
|||
<ng-form name="aggForm">
|
||||
<!-- header -->
|
||||
<div class="vis-editor-agg-header">
|
||||
|
||||
<!-- header -->
|
||||
<div class="vis-editor-agg-header">
|
||||
<!-- open/close editor -->
|
||||
<button
|
||||
aria-label="{{ editorOpen ? 'Close Editor' : 'Open Editor' }}"
|
||||
ng-click="editorOpen = !editorOpen"
|
||||
type="button"
|
||||
class="btn btn-xs vis-editor-agg-header-toggle">
|
||||
<i aria-hidden="true" ng-class="{ 'fa-caret-down': editorOpen, 'fa-caret-right': !editorOpen }" class="fa"></i>
|
||||
</button>
|
||||
|
||||
<!-- open/close editor -->
|
||||
<!-- title -->
|
||||
<span class="vis-editor-agg-header-title">
|
||||
{{ agg.schema.title }}
|
||||
</span>
|
||||
|
||||
<!-- description -->
|
||||
<span ng-if="!editorOpen && aggForm.$valid" class="vis-editor-agg-header-description">
|
||||
{{ describe() }}
|
||||
</span>
|
||||
|
||||
<!-- error -->
|
||||
<span ng-if="!editorOpen && aggForm.$invalid" class="vis-editor-agg-header-description danger">
|
||||
{{ aggForm.describeErrors() }}
|
||||
</span>
|
||||
|
||||
<!-- controls !!!actually disabling buttons will break tooltips¡¡¡ -->
|
||||
<div class="vis-editor-agg-header-controls btn-group">
|
||||
<!-- up button -->
|
||||
<button
|
||||
aria-label="{{ editorOpen ? 'Close Editor' : 'Open Editor' }}"
|
||||
ng-click="editorOpen = !editorOpen"
|
||||
aria-label="Increase Priority"
|
||||
ng-if="stats.count > 1"
|
||||
ng-class="{ disabled: $first }"
|
||||
ng-click="moveUp(agg)"
|
||||
tooltip="Increase Priority"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs vis-editor-agg-header-toggle">
|
||||
<i aria-hidden="true" ng-class="{ 'fa-caret-down': editorOpen, 'fa-caret-right': !editorOpen }" class="fa"></i>
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-caret-up"></i>
|
||||
</button>
|
||||
|
||||
<!-- title -->
|
||||
<span class="vis-editor-agg-header-title">
|
||||
{{ agg.schema.title }}
|
||||
</span>
|
||||
<!-- down button -->
|
||||
<button
|
||||
aria-lebl="Decrease Priority"
|
||||
ng-if="stats.count > 1"
|
||||
ng-class="{ disabled: $last }"
|
||||
ng-click="moveDown(agg)"
|
||||
tooltip="Decrease Priority"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
|
||||
<!-- description -->
|
||||
<span ng-if="!editorOpen && aggForm.$valid" class="vis-editor-agg-header-description">
|
||||
{{ describe() }}
|
||||
</span>
|
||||
|
||||
<!-- error -->
|
||||
<span ng-if="!editorOpen && aggForm.$invalid" class="vis-editor-agg-header-description danger">
|
||||
{{ aggForm.describeErrors() }}
|
||||
</span>
|
||||
|
||||
<!-- controls !!!actually disabling buttons will break tooltips¡¡¡ -->
|
||||
<div class="vis-editor-agg-header-controls btn-group">
|
||||
<!-- up button -->
|
||||
<button
|
||||
aria-label="Increase Priority"
|
||||
ng-if="stats.count > 1"
|
||||
ng-class="{ disabled: $first }"
|
||||
ng-click="moveUp(agg)"
|
||||
tooltip="Increase Priority"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-caret-up"></i>
|
||||
</button>
|
||||
|
||||
<!-- down button -->
|
||||
<button
|
||||
aria-lebl="Decrease Priority"
|
||||
ng-if="stats.count > 1"
|
||||
ng-class="{ disabled: $last }"
|
||||
ng-click="moveDown(agg)"
|
||||
tooltip="Decrease Priority"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
|
||||
<!-- remove button -->
|
||||
<button
|
||||
ng-if="canRemove(agg)"
|
||||
aria-label="Remove Dimension"
|
||||
ng-if="stats.count > stats.min"
|
||||
ng-click="remove(agg)"
|
||||
tooltip="Remove Dimension"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs btn-danger">
|
||||
<i aria-hidden="true" class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- remove button -->
|
||||
<button
|
||||
ng-if="canRemove(agg)"
|
||||
aria-label="Remove Dimension"
|
||||
ng-if="stats.count > stats.min"
|
||||
ng-click="remove(agg)"
|
||||
tooltip="Remove Dimension"
|
||||
tooltip-append-to-body="true"
|
||||
type="button"
|
||||
class="btn btn-xs btn-danger">
|
||||
<i aria-hidden="true" class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<vis-editor-agg-params
|
||||
agg="agg"
|
||||
group-name="groupName"
|
||||
ng-show="editorOpen"
|
||||
class="vis-editor-agg-editor">
|
||||
</vis-editor-agg-params>
|
||||
<vis-editor-agg-params
|
||||
agg="agg"
|
||||
group-name="groupName"
|
||||
ng-show="editorOpen"
|
||||
class="vis-editor-agg-editor">
|
||||
</vis-editor-agg-params>
|
||||
|
||||
<vis-editor-agg-add
|
||||
ng-if="$index + 1 === stats.count"
|
||||
class="vis-editor-agg-add vis-editor-agg-add-subagg">
|
||||
</vis-editor-agg-add>
|
||||
</ng-form>
|
||||
<vis-editor-agg-add
|
||||
ng-if="$index + 1 === stats.count"
|
||||
class="vis-editor-agg-add vis-editor-agg-add-subagg">
|
||||
</vis-editor-agg-add>
|
||||
|
|
|
@ -15,10 +15,14 @@ define(function (require) {
|
|||
});
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
restrict: 'A',
|
||||
template: require('text!plugins/visualize/editor/agg.html'),
|
||||
link: function ($scope, $el) {
|
||||
$scope.editorOpen = $scope.agg.brandNew;
|
||||
require: 'form',
|
||||
link: function ($scope, $el, attrs, kbnForm) {
|
||||
$scope.editorOpen = !!$scope.agg.brandNew;
|
||||
if (!$scope.editorOpen) {
|
||||
$scope.$evalAsync(kbnForm.$setTouched);
|
||||
}
|
||||
|
||||
$scope.$watchMulti([
|
||||
'$index',
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
</nesting-indicator>
|
||||
|
||||
<!-- agg.html - controls for aggregation -->
|
||||
<vis-editor-agg class="vis-editor-agg"></vis-editor-agg>
|
||||
<ng-form vis-editor-agg name="aggForm" class="vis-editor-agg"></ng-form>
|
||||
</div>
|
||||
|
||||
<vis-editor-agg-add ng-if="stats.count === 0" class="vis-editor-agg-add"></vis-editor-agg-add>
|
||||
</div>
|
||||
</li>
|
||||
</li>
|
||||
|
|
|
@ -9,30 +9,26 @@
|
|||
|
||||
<nav class="navbar navbar-default navbar-static-top subnav">
|
||||
<div class="container-fluid">
|
||||
<ul class="nav navbar-nav" ng-init="visConfigSection = vis.type.requiresSearch ? 'data' : 'options'">
|
||||
<li ng-class="{active: visConfigSection == 'data'}" ng-show="vis.type.schemas.metrics">
|
||||
<a class="navbar-link active" ng-click="visConfigSection='data'">Data</a>
|
||||
|
||||
<!-- tabs -->
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{active: sidebar.section == 'data'}" ng-show="vis.type.schemas.metrics">
|
||||
<a class="navbar-link active" ng-click="sidebar.section='data'">Data</a>
|
||||
</li>
|
||||
<li ng-class="{active: visConfigSection == 'options'}">
|
||||
<a class="navbar-link" ng-click="visConfigSection='options'">Options</a>
|
||||
<li ng-class="{active: sidebar.section == 'options'}">
|
||||
<a class="navbar-link" ng-click="sidebar.section='options'">Options</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- controls -->
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ng-if="visualizeEditor.softErrorCount() > 0"
|
||||
disabled
|
||||
tooltip="{{visualizeEditor.describeErrors()}}" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
tooltip="{{ visualizeEditor.describeErrors() }}" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<a class="danger navbar-link">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li tooltip="Discard changes" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<button class="btn-default navbar-btn-link"
|
||||
ng-disabled="!vis.dirty"
|
||||
ng-click="resetEditableVis()">
|
||||
|
||||
<i class="fa fa-close"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li tooltip="Apply changes" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<button class="btn-success navbar-btn-link"
|
||||
type="submit"
|
||||
|
@ -41,11 +37,19 @@
|
|||
<i class="fa fa-play"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li tooltip="Discard changes" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<button class="btn-default navbar-btn-link"
|
||||
ng-disabled="!vis.dirty"
|
||||
ng-click="resetEditableVis()">
|
||||
|
||||
<i class="fa fa-close"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="vis-editor-config" ng-show="visConfigSection == 'data'">
|
||||
<div class="vis-editor-config" ng-show="sidebar.section == 'data'">
|
||||
<ul class="list-unstyled">
|
||||
<!-- metrics -->
|
||||
<vis-editor-agg-group ng-if="vis.type.schemas.metrics" group-name="metrics"></vis-editor-agg-group>
|
||||
|
@ -55,16 +59,12 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="vis-editor-config" ng-show="visConfigSection == 'options'">
|
||||
<div class="vis-editor-config" ng-show="sidebar.section == 'options'">
|
||||
<ul class="list-unstyled">
|
||||
|
||||
<!-- vis options -->
|
||||
<vis-editor-vis-options
|
||||
vis="vis"
|
||||
>
|
||||
</vis-editor-vis-options>
|
||||
<vis-editor-vis-options vis="vis"></vis-editor-vis-options>
|
||||
|
||||
<!-- apply/discard -->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,9 +11,11 @@ define(function (require) {
|
|||
restrict: 'E',
|
||||
template: require('text!plugins/visualize/editor/sidebar.html'),
|
||||
scope: true,
|
||||
link: function ($scope) {
|
||||
controllerAs: 'sidebar',
|
||||
controller: function ($scope) {
|
||||
$scope.$bind('vis', 'editableVis');
|
||||
this.section = _.get($scope, 'vis.type.requiresSearch') ? 'data' : 'options';
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,29 +3,36 @@ define(function () {
|
|||
var lineSize = 0;
|
||||
var newText = '';
|
||||
var inHtmlTag = false;
|
||||
var inHtmlChar = false;
|
||||
|
||||
for (var i = 0, len = text.length; i < len; i++) {
|
||||
var chr = text.charAt(i);
|
||||
newText += chr;
|
||||
|
||||
switch (chr) {
|
||||
case ' ':
|
||||
case ';':
|
||||
case ':':
|
||||
case ',':
|
||||
// natural line break, reset line size
|
||||
lineSize = 0;
|
||||
break;
|
||||
case '<':
|
||||
inHtmlTag = true;
|
||||
break;
|
||||
case '>':
|
||||
inHtmlTag = false;
|
||||
lineSize = 0;
|
||||
break;
|
||||
default:
|
||||
if (!inHtmlTag) lineSize++;
|
||||
break;
|
||||
case ' ':
|
||||
case ':':
|
||||
case ',':
|
||||
// natural line break, reset line size
|
||||
lineSize = 0;
|
||||
break;
|
||||
case '<':
|
||||
inHtmlTag = true;
|
||||
break;
|
||||
case '>':
|
||||
inHtmlTag = false;
|
||||
lineSize = 0;
|
||||
break;
|
||||
case '&':
|
||||
inHtmlChar = true;
|
||||
break;
|
||||
case ';':
|
||||
inHtmlChar = false;
|
||||
lineSize = 0;
|
||||
break;
|
||||
default:
|
||||
if (!inHtmlTag && !inHtmlChar) lineSize++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lineSize > minLineLength) {
|
||||
|
@ -38,4 +45,4 @@ define(function () {
|
|||
|
||||
return newText;
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
68
src/kibana/utils/range.js
Normal file
68
src/kibana/utils/range.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* Regexp portion that matches our number
|
||||
*
|
||||
* supports:
|
||||
* -100
|
||||
* -100.0
|
||||
* 0
|
||||
* 0.10
|
||||
* Infinity
|
||||
* -Infinity
|
||||
*
|
||||
* @type {String}
|
||||
*/
|
||||
var _RE_NUMBER = '(\\-?(?:\\d+(?:\\.\\d+)?|Infinity))';
|
||||
|
||||
/**
|
||||
* Regexp for the interval notation
|
||||
*
|
||||
* supports:
|
||||
* [num, num]
|
||||
* ( num , num ]
|
||||
* [Infinity,num)
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
var RANGE_RE = new RegExp('^\\s*([\\[|\\(])\\s*' + _RE_NUMBER + '\\s*,\\s*' + _RE_NUMBER + '\\s*([\\]|\\)])\\s*$');
|
||||
|
||||
function parse(input) {
|
||||
|
||||
var match = String(input).match(RANGE_RE);
|
||||
if (!match) {
|
||||
throw new TypeError('expected input to be in interval notation eg. (100, 200]');
|
||||
}
|
||||
|
||||
return new Range(
|
||||
match[1] === '[',
|
||||
parseFloat(match[2]),
|
||||
parseFloat(match[3]),
|
||||
match[4] === ']'
|
||||
);
|
||||
}
|
||||
|
||||
function Range(/* minIncl, min, max, maxIncl */) {
|
||||
var args = _.toArray(arguments);
|
||||
if (args[1] > args[2]) args.reverse();
|
||||
|
||||
this.minInclusive = args[0];
|
||||
this.min = args[1];
|
||||
this.max = args[2];
|
||||
this.maxInclusive = args[3];
|
||||
}
|
||||
|
||||
Range.prototype.within = function (n) {
|
||||
if (this.min === n && !this.minInclusive) return false;
|
||||
if (this.min > n) return false;
|
||||
|
||||
if (this.max === n && !this.maxInclusive) return false;
|
||||
if (this.max < n) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return parse;
|
||||
|
||||
});
|
|
@ -1,12 +1,5 @@
|
|||
module.exports = function (grunt) {
|
||||
var config = {
|
||||
test: {
|
||||
files: [
|
||||
'<%= unitTestDir %>/**/*.js'
|
||||
],
|
||||
tasks: ['mocha:unit']
|
||||
},
|
||||
|
||||
less: {
|
||||
files: [
|
||||
'<%= app %>/**/styles/**/*.less',
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
define(function (require) {
|
||||
describe('PercentList directive', function () {
|
||||
describe('NumberList directive', function () {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
var simulateKeys = require('test_utils/simulate_keys');
|
||||
|
||||
require('components/agg_types/controls/_values_list');
|
||||
require('components/number_list/number_list');
|
||||
|
||||
var $el;
|
||||
var $scope;
|
||||
var compile;
|
||||
|
||||
function onlyValidValues() {
|
||||
return $el.find('[ng-model]').toArray().map(function (el) {
|
||||
var ngModel = $(el).controller('ngModel');
|
||||
return ngModel.$valid ? ngModel.$modelValue : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function ($injector) {
|
||||
var $compile = $injector.get('$compile');
|
||||
var $rootScope = $injector.get('$rootScope');
|
||||
|
||||
$scope = $rootScope.$new();
|
||||
$el = $('<div>').append(
|
||||
$('<input>')
|
||||
.attr('ng-model', 'vals[$index]')
|
||||
.attr('ng-repeat', 'val in vals')
|
||||
.attr('values-list', 'vals')
|
||||
.attr('values-list-min', '0')
|
||||
.attr('values-list-max', '100')
|
||||
);
|
||||
compile = function (vals) {
|
||||
$el = $('<kbn-number-list ng-model="vals">');
|
||||
compile = function (vals, range) {
|
||||
$scope.vals = vals || [];
|
||||
$el.attr('range', range);
|
||||
|
||||
$compile($el)($scope);
|
||||
$scope.$apply();
|
||||
};
|
||||
|
@ -38,51 +40,22 @@ define(function (require) {
|
|||
|
||||
it('fails on invalid numbers', function () {
|
||||
compile([1, 'foo']);
|
||||
expect($scope.vals).to.eql([1, undefined]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(1);
|
||||
expect(onlyValidValues()).to.eql([1, undefined]);
|
||||
});
|
||||
|
||||
it('supports decimals', function () {
|
||||
compile(['1.2', '000001.6', '99.10']);
|
||||
expect($scope.vals).to.eql([1.2, 1.6, 99.1]);
|
||||
expect(onlyValidValues()).to.eql([1.2, 1.6, 99.1]);
|
||||
});
|
||||
|
||||
it('ensures that the values are in order', function () {
|
||||
compile([1, 2, 3, 10, 4, 5]);
|
||||
expect($scope.vals).to.eql([1, 2, 3, undefined, 4, 5]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(1);
|
||||
expect(onlyValidValues()).to.eql([1, 2, 3, 10, undefined, 5]);
|
||||
});
|
||||
|
||||
describe('ensures that the values are between 0 and 100', function () {
|
||||
it(': -1', function () {
|
||||
compile([-1, 1]);
|
||||
expect($scope.vals).to.eql([undefined, 1]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(1);
|
||||
});
|
||||
|
||||
it(': 0', function () {
|
||||
compile([0, 1]);
|
||||
expect($scope.vals).to.eql([undefined, 1]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(1);
|
||||
});
|
||||
|
||||
it(': 0.0001', function () {
|
||||
compile([0.0001, 1]);
|
||||
expect($scope.vals).to.eql([0.0001, 1]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(0);
|
||||
});
|
||||
|
||||
it(': 99.9999999', function () {
|
||||
compile([1, 99.9999999]);
|
||||
expect($scope.vals).to.eql([1, 99.9999999]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(0);
|
||||
});
|
||||
|
||||
it(': 101', function () {
|
||||
compile([1, 101]);
|
||||
expect($scope.vals).to.eql([1, undefined]);
|
||||
expect($el.find('.ng-invalid').size()).to.be(1);
|
||||
});
|
||||
it('requires that values are within a range', function () {
|
||||
compile([50, 100, 200, 250], '[100, 200)');
|
||||
expect(onlyValidValues()).to.eql([undefined, 100, undefined, undefined]);
|
||||
});
|
||||
|
||||
describe('listens for keyboard events', function () {
|
||||
|
@ -94,7 +67,7 @@ define(function (require) {
|
|||
['up', 'up', 'up']
|
||||
)
|
||||
.then(function () {
|
||||
expect($scope.vals).to.eql([4]);
|
||||
expect(onlyValidValues()).to.eql([4]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -118,7 +91,7 @@ define(function (require) {
|
|||
seq
|
||||
)
|
||||
.then(function () {
|
||||
expect($scope.vals).to.eql([5.1]);
|
||||
expect(onlyValidValues()).to.eql([5.1]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -130,7 +103,7 @@ define(function (require) {
|
|||
['down', 'down', 'down']
|
||||
)
|
||||
.then(function () {
|
||||
expect($scope.vals).to.eql([2]);
|
||||
expect(onlyValidValues()).to.eql([2]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -154,7 +127,7 @@ define(function (require) {
|
|||
seq
|
||||
)
|
||||
.then(function () {
|
||||
expect($scope.vals).to.eql([4.8]);
|
||||
expect(onlyValidValues()).to.eql([4.8]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -170,9 +143,9 @@ define(function (require) {
|
|||
|
||||
return simulateKeys(getEl, seq)
|
||||
.then(function () {
|
||||
expect($scope.vals).to.eql([9, 10, 13]);
|
||||
expect(onlyValidValues()).to.eql([9, 10, 13]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -64,7 +64,7 @@ define(function (require) {
|
|||
|
||||
// make the element
|
||||
$elem = angular.element(
|
||||
'<vis-editor-agg></vis-editor-agg>'
|
||||
'<ng-form vis-editor-agg></ng-form>'
|
||||
);
|
||||
|
||||
// compile the html
|
||||
|
@ -77,7 +77,7 @@ define(function (require) {
|
|||
$scope = $elem.isolateScope();
|
||||
}));
|
||||
|
||||
it('should only add the close button only if there is more than the minimum', function () {
|
||||
it('should only add the close button if there is more than the minimum', function () {
|
||||
expect($parentScope.canRemove($parentScope.agg)).to.be(false);
|
||||
$parentScope.group.push({
|
||||
id: '3',
|
||||
|
|
|
@ -8,11 +8,11 @@ define(function (require) {
|
|||
['aaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaa<wbr>aaaaaaaaa'],
|
||||
['aaaa aaaaaaaaaaaaaaa', 'aaaa aaaaaaaaaaa<wbr>aaaa'],
|
||||
['aaaa;aaaaaaaaaaaaaaa', 'aaaa;aaaaaaaaaaa<wbr>aaaa'],
|
||||
['aaaa&aaaaaaaaaaaaaaa', 'aaaa&aaaaaa<wbr>aaaaaaaaa'],
|
||||
['aaaa:aaaaaaaaaaaaaaa', 'aaaa:aaaaaaaaaaa<wbr>aaaa'],
|
||||
['aaaa,aaaaaaaaaaaaaaa', 'aaaa,aaaaaaaaaaa<wbr>aaaa'],
|
||||
['aaaa aaaa', 'aaaa aaaa'],
|
||||
['aaaa <mark>aaaa</mark>aaaaaaaaaaaa', 'aaaa <mark>aaaa</mark>aaaaaaaaaaa<wbr>a']
|
||||
['aaaa <mark>aaaa</mark>aaaaaaaaaaaa', 'aaaa <mark>aaaa</mark>aaaaaaaaaaa<wbr>a'],
|
||||
['aaaa"aaaaaaaaaaaa', 'aaaa"aaaaaaaaaaa<wbr>a']
|
||||
];
|
||||
|
||||
_.each(fixtures, function (fixture) {
|
||||
|
|
110
test/unit/specs/utils/range.js
Normal file
110
test/unit/specs/utils/range.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
define(function (require) {
|
||||
describe('Range parsing utility', function () {
|
||||
var _ = require('lodash');
|
||||
var parse = require('utils/range');
|
||||
|
||||
it('throws an error for inputs that are not formatted properly', function () {
|
||||
expect(function () {
|
||||
parse('');
|
||||
}).to.throwException(TypeError);
|
||||
|
||||
expect(function () {
|
||||
parse('p10202');
|
||||
}).to.throwException(TypeError);
|
||||
|
||||
expect(function () {
|
||||
parse('{0,100}');
|
||||
}).to.throwException(TypeError);
|
||||
|
||||
expect(function () {
|
||||
parse('[0,100');
|
||||
}).to.throwException(TypeError);
|
||||
|
||||
expect(function () {
|
||||
parse(')0,100(');
|
||||
}).to.throwException(TypeError);
|
||||
});
|
||||
|
||||
var tests = {
|
||||
'[ 0 , 100 ]': {
|
||||
props: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
minInclusive: true,
|
||||
maxInclusive: true
|
||||
},
|
||||
within: [
|
||||
[0, true],
|
||||
[0.0000001, true],
|
||||
[1, true],
|
||||
[99.99999, true],
|
||||
[100, true]
|
||||
]
|
||||
},
|
||||
'(26.3 , 42]': {
|
||||
props: {
|
||||
min: 26.3,
|
||||
max: 42,
|
||||
minInclusive: false,
|
||||
maxInclusive: true
|
||||
},
|
||||
within: [
|
||||
[26.2999999, false],
|
||||
[26.3000001, true],
|
||||
[30, true],
|
||||
[41, true],
|
||||
[42, true]
|
||||
]
|
||||
},
|
||||
'(-50,50)': {
|
||||
props: {
|
||||
min: -50,
|
||||
max: 50,
|
||||
minInclusive: false,
|
||||
maxInclusive: false
|
||||
},
|
||||
within: [
|
||||
[-50, false],
|
||||
[-49.99999, true],
|
||||
[0, true],
|
||||
[49.99999, true],
|
||||
[50, false]
|
||||
]
|
||||
},
|
||||
'(Infinity, -Infinity)': {
|
||||
props: {
|
||||
min: -Infinity,
|
||||
max: Infinity,
|
||||
minInclusive: false,
|
||||
maxInclusive: false
|
||||
},
|
||||
within: [
|
||||
[0, true],
|
||||
[-0.0000001, true],
|
||||
[-1, true],
|
||||
[-10000000000, true],
|
||||
[-Infinity, false],
|
||||
[Infinity, false],
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
_.forOwn(tests, function (spec, str) {
|
||||
|
||||
describe(str, function () {
|
||||
var range = parse(str);
|
||||
|
||||
it('creation', function () {
|
||||
expect(range).to.eql(spec.props);
|
||||
});
|
||||
|
||||
spec.within.forEach(function (tup) {
|
||||
it('#within(' + tup[0] + ')', function () {
|
||||
expect(range.within(tup[0])).to.be(tup[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue