Merge branch 'master' of github.com:elasticsearch/kibana into pr/3879

This commit is contained in:
Rashid Khan 2015-05-29 08:58:18 -07:00
commit 81d8326c2d
99 changed files with 1835 additions and 1346 deletions

View file

@ -22,7 +22,7 @@ Please make sure you have signed the [Contributor License Agreement](http://www.
```sh
npm install -g grunt-cli bower
```
- Clone the kibana repo and move into it
```sh
@ -74,4 +74,31 @@ Distributable, built packages can be found in `target/` after the build complete
Push your local changes to your forked copy of the repository and submit a pull request. In the pull request, describe what your changes do and mention the number of the issue where discussion has taken place, eg “Closes #123″.
Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we would love to work with you to get your pull request merged into Kibana.
Always submit your pull against `master` unless the bug is only present in an older version. If the bug effects both `master` and another branch say so in your pull.
Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we'll work with you to get your pull request merged into Kibana.
### The road to review
After a pull is submitted, it needs to get to review. If you have commit permission on the Kibana repo you will probably perform these steps while submitting your pull request. If not, a member of the elastic organization will do them for you, though you can help by suggesting a reviewer for your changes if you've interacted with someone while working on the issue.
1. Assign the `review` tag. This signals to the team that someone needs to give this attention.
1. Assign version tags. If the pull is related to an existing issue (and it should be!), that issue probably has a version tag (eg `4.0.1`) on it. Assign the same version tag to your pull. You may end up with 2 or more version tags if the changes requires backporting
1. Find someone to review your pull. Don't just pick any yahoo, pick the right person. The right person might be the original reporter of the issue, but it might also be the person most familiar with the code you've changed. If neither of those things apply, or your change is small in scope, try to find someone on the Kibana team without a ton of existing reviews on their plate. As a rule, most pulls will require 2 reviewers, but the first reviewer will pick the 2nd.
### Review engaged
So, you've been assigned a pull to review. What's that look like?
Remember, someone is blocked by a pull awaiting review, make it count. Be thorough, the more action items you catch in the first review, the less back and forth will be required, and the better chance the pull has of being successful. Don't you like success?
1. **Understand the issue** that is being fixed, or the feature being added. Check the description on the pull, and check out the related issue. If you don't understand something, ask the person the submitter for clarification.
1. **Reproduce the bug** (or the lack of feature I guess?) in the destination branch, usually `master`. The referenced issue will help you here. If you're unable to reproduce the issue, contact the issue submitter for clarification
1. **Check out the pull** and test it. Is the issue fixed? Does it have nasty side effects? Try to create suspect inputs. If it operates on the value of a field try things like: strings (including an empty string), null, numbers, dates. Try to think of edge cases that might break the code.
1. **Read the code**. Understanding the changes will help you find additional things to test. Contact the submitter if you don't understand something.
1. **Go line-by-line**. Are there [style guide](https://github.com/elastic/kibana/blob/master/STYLEGUIDE.md) violations? Strangely named variables? Magic numbers? Do the abstractions make sense to you? Are things arranged in a testable way?
1. **Speaking of tests** Are they there? If a new function was added does it have tests? Do the tests, well, TEST anything? Do they just run the function or do they properly check the output?
1. **Suggest improvements** If there are changes needed, be explicit, comment on the lines in the code that you'd like changed. You might consider suggesting fixes. If you can't identify the problem, animated screenshots can help the review understand what's going on.
1. **Hand it back** If you found issues, re-assign the submitter to the pull to address them. Repeat until mergable.
1. **Hand it off** If you're the first reviewer and everything looks good but the changes are more than a few lines, hand the pull to someone else to take a second look. Again, try to find the right person to assign it to.
1. **Merge the code** When everything looks good, merge into the target branch. Check the labels on the pull to see if backporting is required, and perform the backport if so.

View file

@ -53,5 +53,8 @@
"numeral": "~1.5.3",
"leaflet-draw": "~0.2.4"
},
"devDependencies": {}
"devDependencies": {},
"resolutions": {
"angular-bootstrap": "~0.12"
}
}

View file

@ -1,51 +0,0 @@
define(function (require) {
var decodeGeoHash = require('utils/decode_geo_hash');
var _ = require('lodash');
function readRows(table, agg, index, chart) {
var geoJson = chart.geoJson;
var props = geoJson.properties;
var metricLabel = agg.metric.makeLabel();
props.length = table.rows.length;
props.min = null;
props.max = null;
props.agg = agg;
table.rows.forEach(function (row) {
var geohash = row[index.geo].value;
var valResult = row[index.metric];
var val = valResult.value;
if (props.min === null || val < props.min) props.min = val;
if (props.max === null || val > props.max) props.max = val;
var location = decodeGeoHash(geohash);
var center = [location.longitude[2], location.latitude[2]];
var rectangle = [
[location.longitude[0], location.latitude[0]],
[location.longitude[1], location.latitude[0]],
[location.longitude[1], location.latitude[1]],
[location.longitude[0], location.latitude[1]]
];
geoJson.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: center
},
properties: {
valueLabel: metricLabel,
count: val,
geohash: geohash,
center: center,
aggConfigResult: valResult,
rectangle: rectangle
}
});
});
}
return readRows;
});

View file

@ -5,4 +5,4 @@
<td>{{detail.value}}</td>
</tr>
</tbody>
</table>
</table>

View file

@ -1,32 +1,41 @@
define(function (require) {
return function TileMapTooltipFormatter($compile, $rootScope) {
return function TileMapTooltipFormatter($compile, $rootScope, Private) {
var $ = require('jquery');
var _ = require('lodash');
var fieldFormats = Private(require('registry/field_formats'));
var $tooltipScope = $rootScope.$new();
var $tooltip = $(require('text!components/agg_response/geo_json/_tooltip.html'));
$compile($tooltip)($tooltipScope);
var $el = $('<div>').html(require('text!components/agg_response/geo_json/_tooltip.html'));
$compile($el)($tooltipScope);
return function tooltipFormatter(feature) {
if (!feature) return '';
var details = $tooltipScope.details = [];
var value = feature.properties.value;
var acr = feature.properties.aggConfigResult;
var vis = acr.aggConfig.vis;
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var metricAgg = acr.aggConfig;
var geoFormat = _.get(vis.aggs, 'byTypeName.geohash_grid[0].format');
if (!geoFormat) geoFormat = fieldFormats.getDefaultInstance('geo_point');
var metric = {
label: feature.properties.valueLabel,
value: feature.properties.count
};
var location = {
label: 'Center',
value: lat.toFixed(4) + ', ' + lng.toFixed(4)
};
details.push(metric, location);
$tooltipScope.details = [
{
label: metricAgg.makeLabel(),
value: metricAgg.fieldFormatter()(value)
},
{
label: 'Center',
value: geoFormat.convert({
lat: feature.geometry.coordinates[1],
lon: feature.geometry.coordinates[0]
})
}
];
$tooltipScope.$apply();
return $tooltip[0].outerHTML;
return $el.html();
};
};
});
});

View file

@ -2,62 +2,41 @@ define(function (require) {
return function TileMapConverterFn(Private, timefilter, $compile, $rootScope) {
var _ = require('lodash');
var readRows = require('components/agg_response/geo_json/_read_rows');
var rowsToFeatures = require('components/agg_response/geo_json/rowsToFeatures');
var tooltipFormatter = Private(require('components/agg_response/geo_json/_tooltip_formatter'));
function findCol(table, name) {
return _.findIndex(table.columns, function (col) {
return col.aggConfig.schema.name === name;
});
}
return function (vis, table) {
function createGeoJson(vis, table) {
var index = {
geo: findCol(table, 'segment'),
metric: findCol(table, 'metric')
};
var col = {
geo: table.columns[index.geo],
metric: table.columns[index.metric],
};
var agg = _.mapValues(col, function (col) {
return col && col.aggConfig;
});
var chart = {};
chart.tooltipFormatter = tooltipFormatter;
var geoJson = chart.geoJson = {
type: 'FeatureCollection',
features: []
};
var props = geoJson.properties = {
label: table.title(),
length: 0,
min: 0,
max: 0
};
if (agg.metric._opts.params && agg.metric._opts.params.field) {
props.metricField = agg.metric._opts.params.field;
function columnIndex(schema) {
return _.findIndex(table.columns, function (col) {
return col.aggConfig.schema.name === schema;
});
}
// set precision from the bucketting column, if we have one
if (agg.geo) {
props.precision = _.parseInt(agg.geo.params.precision);
}
var geoI = columnIndex('segment');
var metricI = columnIndex('metric');
var geoAgg = _.get(table.columns, [geoI, 'aggConfig']);
var metricAgg = _.get(table.columns, [metricI, 'aggConfig']);
// we're all done if there are no columns
if (!col.geo || !col.metric || !table.rows.length) return chart;
var features = rowsToFeatures(table, geoI, metricI);
var values = features.map(function (feature) {
return feature.properties.value;
});
// read the rows into the geoJson features list
readRows(table, agg, index, chart);
return chart;
}
return createGeoJson;
return {
title: table.title(),
valueFormatter: metricAgg && metricAgg.fieldFormatter(),
tooltipFormatter: tooltipFormatter,
geohashGridAgg: geoAgg,
geoJson: {
type: 'FeatureCollection',
features: features,
properties: {
min: _.min(values),
max: _.max(values)
}
}
};
};
};
});

View file

@ -0,0 +1,50 @@
define(function (require) {
var decodeGeoHash = require('utils/decode_geo_hash');
var AggConfigResult = require('components/vis/_agg_config_result');
var _ = require('lodash');
function getAcr(val) {
return val instanceof AggConfigResult ? val : null;
}
function unwrap(val) {
return getAcr(val) ? val.value : val;
}
function convertRowsToFeatures(table, geoI, metricI) {
return _.transform(table.rows, function (features, row) {
var geohash = unwrap(row[geoI]);
if (!geohash) return;
var location = decodeGeoHash(geohash);
var center = [
location.longitude[2],
location.latitude[2]
];
var rectangle = [
[location.longitude[0], location.latitude[0]],
[location.longitude[1], location.latitude[0]],
[location.longitude[1], location.latitude[1]],
[location.longitude[0], location.latitude[1]]
];
features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: center
},
properties: {
geohash: geohash,
value: unwrap(row[metricI]),
aggConfigResult: getAcr(row[metricI]),
center: center,
rectangle: rectangle
}
});
}, []);
}
return convertRowsToFeatures;
});

View file

@ -13,19 +13,12 @@ define(function () {
|| (col && col.label)
|| ('level ' + item.depth);
// Set the bucket name, and use the converter to format the field if
// the field exists.
var bucket = item.name;
if (col) {
bucket = col.fieldFormatter()(bucket);
}
// Add the row to the tooltipScope.rows
memo.unshift({
aggConfig: col,
depth: depth,
field: field,
bucket: bucket,
bucket: item.name,
metric: item.size,
item: item
});

View file

@ -16,6 +16,8 @@ define(function (require) {
// Collect the current leaf and parents into an array of values
$tooltipScope.rows = collectBranch(datum);
var metricCol = $tooltipScope.metricCol = _.find(columns, { categoryName: 'metric' });
// Map those values to what the tooltipSource.rows format.
_.forEachRight($tooltipScope.rows, function (row, i, rows) {
row.spacer = $sce.trustAsHtml(_.repeat('&nbsp;', row.depth));
@ -29,6 +31,8 @@ define(function (require) {
percent = row.item.percentOfGroup;
}
row.metric = metricCol.aggConfig.fieldFormatter()(row.metric);
if (percent != null) {
row.metric += ' (' + numeral(percent).format('0.[00]%') + ')';
}
@ -36,8 +40,6 @@ define(function (require) {
return row;
});
$tooltipScope.metricCol = _.find(columns, { categoryName: 'metric' });
$tooltipScope.$apply();
return $tooltip[0].outerHTML;
};

View file

@ -5,19 +5,16 @@ define(function (require) {
var AggConfigResult = require('components/vis/_agg_config_result');
return function transformAggregation(agg, metric, aggData, parent) {
return _.map(extractBuckets(aggData), function (bucket) {
// Pick the appropriate value, if the metric doesn't exist then we just
// use the count.
var value = bucket.doc_count;
if (bucket[metric.id] && !_.isUndefined(bucket[metric.id].value)) {
value = bucket[metric.id].value;
}
var aggConfigResult = new AggConfigResult(
agg,
parent && parent.aggConfigResult,
metric.getValue(bucket),
agg.getKey(bucket)
);
// Create the new branch record
var $parent = parent && parent.aggConfigResult;
var aggConfigResult = new AggConfigResult(agg, $parent, value, agg.getKey(bucket));
var branch = {
name: bucket.key,
size: value,
name: agg.fieldFormatter()(bucket.key),
size: aggConfigResult.value,
aggConfig: agg,
aggConfigResult: aggConfigResult
};

View file

@ -21,6 +21,7 @@ define(function (require) {
}
require('filters/field_type');
require('components/validateDateInterval');
return new BucketAggType({
name: 'date_histogram',
@ -59,6 +60,10 @@ define(function (require) {
return agg.vis.indexPattern.timeFieldName;
},
onChange: function (agg) {
if (_.get(agg, 'params.interval.val') === 'auto' && !agg.fieldIsTimeField()) {
delete agg.params.interval;
}
setBounds(agg, true);
}
},
@ -85,7 +90,7 @@ define(function (require) {
var scaleMetrics = interval.scaled && interval.scale < 1;
if (scaleMetrics) {
scaleMetrics = _.every(agg.vis.aggs.bySchemaGroup.metrics, function (agg) {
return agg.type.name === 'count' || agg.type.name === 'sum';
return agg.type && (agg.type.name === 'count' || agg.type.name === 'sum');
});
}

View file

@ -5,6 +5,8 @@ define(function (require) {
var BucketAggType = Private(require('components/agg_types/buckets/_bucket_agg_type'));
var createFilter = Private(require('components/agg_types/buckets/create_filter/histogram'));
require('components/validateDateInterval');
return new BucketAggType({
name: 'histogram',
title: 'Histogram',

View file

@ -20,8 +20,10 @@
</select>
<input
type="text"
name="customInterval"
ng-model="agg.params.customInterval"
ng-change="agg.write()"
validate-date-interval
ng-change="aggForm.customInterval.$valid && agg.write()"
ng-if="agg.params.interval.val == 'custom'"
class="form-control"
required />

View file

@ -245,9 +245,16 @@ define(function (require) {
return docSource.doCreate(source)
.then(finish)
.catch(function (err) {
var confirmMessage = 'Are you sure you want to overwrite ' + self.title + '?';
if (_.deepGet(err, 'origError.status') === 409 && window.confirm(confirmMessage)) {
return docSource.doIndex(source).then(finish);
// record exists, confirm overwriting
if (_.deepGet(err, 'origError.status') === 409) {
var confirmMessage = 'Are you sure you want to overwrite ' + self.title + '?';
if (window.confirm(confirmMessage)) {
return docSource.doIndex(source).then(finish);
}
// if the user doesn't overwrite record, just swallow the error
return;
}
return Promise.reject(err);
});

View file

@ -16,10 +16,10 @@ define(function (require) {
// The mappers to apply. Each mapper will either return
// a result object with a key and value attribute or
// undefined. If undefined is return then the next
// mapper will get the oppertunity to map the filter.
// mapper will get the opportunity to map the filter.
// To create a new mapper you just need to create a function
// that either handles the mapping opperation or not
// and add it here.
// that either handles the mapping operation or not
// and add it here. ProTip: These are executed in order listed
var mappers = [
Private(require('./mapTerms')),
Private(require('./mapRange')),
@ -28,7 +28,7 @@ define(function (require) {
Private(require('./mapQueryString')),
Private(require('./mapGeoBoundingBox')),
Private(require('./mapScript')),
Private(require('./mapDefault')) // ProTip: last one to get applied
Private(require('./mapDefault'))
];
var noop = function () {

View file

@ -1,18 +1,17 @@
define(function (require) {
var _ = require('lodash');
var makeComparable = function (filter) {
return _.omit(filter, ['$state', '$$hashKey']);
};
var compareFilters = require('components/filter_bar/lib/compareFilters');
var compareOptions = { disabled: true, negate: true };
/**
* Checks to see if only disabled filters have been changed
* @returns {bool} Only disabled filters
*/
return function (newFilters, oldFilters) {
var comparableOldFilters = _.map(oldFilters, makeComparable);
return _.every(newFilters, function (newFilter, i) {
var match = _.find(comparableOldFilters, makeComparable(newFilter));
var match = _.find(oldFilters, function (oldFilter) {
return compareFilters(newFilter, oldFilter, compareOptions);
});
return !!match;
});
};

View file

@ -7,6 +7,7 @@ define(function (require) {
var onlyStateChanged = require('components/filter_bar/lib/onlyStateChanged');
var uniqFilters = require('components/filter_bar/lib/uniqFilters');
var compareFilters = require('components/filter_bar/lib/compareFilters');
var mapAndFlattenFilters = Private(require('components/filter_bar/lib/mapAndFlattenFilters'));
var queryFilter = new EventEmitter();
@ -33,7 +34,7 @@ define(function (require) {
* Adds new filters to the scope and state
* @param {object|array} fitlers Filter(s) to add
* @param {bool} global Should be added to global state
* @returns {object} Resulting new filter list
* @retuns {Promise} filter map promise
*/
queryFilter.addFilters = function (filters, global) {
var appState = getAppState();
@ -43,43 +44,51 @@ define(function (require) {
filters = [filters];
}
if (global) {
// simply concat global filters, they will be deduped
globalState.filters = globalState.filters.concat(filters);
} else if (appState) {
if (!appState.filters) appState.filters = [];
var mergeOptions = { disabled: true, negate: false };
var appFilters = appState.filters.concat(filters);
var merged = mergeAndMutateFilters(globalState.filters, appFilters, mergeOptions);
globalState.filters = merged[0];
appState.filters = merged[1];
}
return saveState();
return mapAndFlattenFilters(filters)
.then(function (filters) {
if (global) {
// simply concat global filters, they will be deduped
globalState.filters = globalState.filters.concat(filters);
} else if (appState) {
if (!appState.filters) appState.filters = [];
appState.filters = appState.filters.concat(filters);
}
});
};
/**
* Removes the filter from the proper state
* @param {object} matchFilter The filter to remove
* @returns {object} Resulting new filter list
*/
queryFilter.removeFilter = function (matchFilter) {
var state = getStateByFilter(matchFilter);
if (!state) return;
var appState = getAppState();
var filter = _.omit(matchFilter, ['$$hashKey']);
var state;
var index;
_.pull(state.filters, matchFilter);
return saveState();
// check for filter in appState
if (appState) {
index = _.findIndex(appState.filters, filter);
if (index !== -1) state = appState;
}
// if not found, check for filter in globalState
if (!state) {
index = _.findIndex(globalState.filters, filter);
if (index !== -1) state = globalState;
else return; // not found in either state, do nothing
}
state.filters.splice(index, 1);
};
/**
* Removes all filters
* @returns {object} Resulting new filter list
*/
queryFilter.removeAll = function () {
var appState = getAppState();
appState.filters = [];
globalState.filters = [];
return saveState();
};
/**
@ -92,16 +101,12 @@ define(function (require) {
// Toggle the disabled flag
var disabled = _.isUndefined(force) ? !filter.meta.disabled : !!force;
filter.meta.disabled = disabled;
// Save the filters back to the searchSource
saveState();
return filter;
};
/**
* Disables all filters
* @params {boolean} force Disable/enable all filters
* @returns {object} Resulting updated filter list
*/
queryFilter.toggleAll = function (force) {
function doToggle(filter) {
@ -109,7 +114,6 @@ define(function (require) {
}
executeOnFilters(doToggle);
return queryFilter.getFilters();
};
@ -121,8 +125,6 @@ define(function (require) {
queryFilter.invertFilter = function (filter) {
// Toggle the negate meta state
filter.meta.negate = !filter.meta.negate;
saveState();
return filter;
};
@ -132,7 +134,6 @@ define(function (require) {
*/
queryFilter.invertAll = function () {
executeOnFilters(queryFilter.invertFilter);
return queryFilter.getFilters();
};
@ -140,7 +141,7 @@ define(function (require) {
* Pins the filter to the global state
* @param {object} filter The filter to pin
* @param {boolean} force pinned state
* @returns {object} filter passed in
* @returns {object} updated filter
*/
queryFilter.pinFilter = function (filter, force) {
var appState = getAppState();
@ -151,25 +152,25 @@ define(function (require) {
if (!_.isArray(appState.filters)) appState.filters = [];
var appIndex = _.indexOf(appState.filters, filter);
var globalIndex = _.indexOf(globalState.filters, filter);
if (appIndex === -1 && globalIndex === -1) return;
if (appIndex !== -1 && force !== false) {
appState.filters.splice(appIndex, 1);
globalState.filters.push(filter);
} else if (globalIndex !== -1 && force !== true) {
} else {
var globalIndex = _.indexOf(globalState.filters, filter);
if (globalIndex === -1 || force === true) return filter;
globalState.filters.splice(globalIndex, 1);
appState.filters.push(filter);
}
saveState();
return filter;
};
/**
* Pins all filters
* @params {boolean} force Pin/Unpin all filters
* @returns {object} Resulting updated filter list
*/
queryFilter.pinAll = function (force) {
function pin(filter) {
@ -177,7 +178,6 @@ define(function (require) {
}
executeOnFilters(pin);
return queryFilter.getFilters();
};
initWatchers();
@ -192,7 +192,6 @@ define(function (require) {
var appState = getAppState();
if (appState) appState.save();
globalState.save();
return queryFilter.getFilters();
}
function appendStoreType(type) {
@ -204,55 +203,42 @@ define(function (require) {
};
}
// get state (app or global) or the filter passed in
function getStateByFilter(filter) {
var appState = getAppState();
if (appState) {
var appIndex = _.indexOf(appState.filters, filter);
if (appIndex !== -1) return appState;
}
var globalIndex = _.indexOf(globalState.filters, filter);
if (globalIndex !== -1) return globalState;
return false;
}
// helper to run a function on all filters in all states
function executeOnFilters(fn) {
var appState = getAppState();
var appFilters;
if (appState && appState.filters) {
appFilters = appState.filters;
} else {
appFilters = [];
}
globalState.filters.concat(appFilters).forEach(fn);
var globalFilters = [];
var appFilters = [];
if (globalState.filters) globalFilters = globalState.filters;
if (appState && appState.filters) appFilters = appState.filters;
globalFilters.concat(appFilters).forEach(fn);
}
function mergeAndMutateFilters(globalFilters, appFilters, compareOptions) {
appFilters = appFilters || [];
globalFilters = globalFilters || [];
compareOptions = _.defaults(compareOptions || {}, { disabled: true, negate: true });
function mergeStateFilters(gFilters, aFilters, compareOptions) {
// ensure we don't mutate the filters passed in
var globalFilters = gFilters ? _.cloneDeep(gFilters) : [];
var appFilters = aFilters ? _.cloneDeep(aFilters) : [];
compareOptions = _.defaults(compareOptions || {}, { disabled: true });
// existing globalFilters should be mutated by appFilters
appFilters = _.filter(appFilters, function (filter) {
_.each(appFilters, function (filter, i) {
var match = _.find(globalFilters, function (globalFilter) {
return compareFilters(globalFilter, filter, compareOptions);
});
// if the filter remains, it doesn't match any filters in global state
if (!match) return true;
// no match, do nothing
if (!match) return;
// filter matches a filter in globalFilters, mutate existing global filter
// matching filter in globalState, update global and remove from appState
_.assign(match.meta, filter.meta);
return false;
appFilters.splice(i, 1);
});
appFilters = uniqFilters(appFilters, { disabled: true });
globalFilters = uniqFilters(globalFilters, { disabled: true });
return [globalFilters, appFilters];
return [
uniqFilters(globalFilters, { disabled: true }),
uniqFilters(appFilters, { disabled: true })
];
}
/**
@ -281,47 +267,67 @@ define(function (require) {
// when states change, use event emitter to trigger updates and fetches
return $rootScope.$watchMulti(stateWatchers, function (next, prev) {
// prevent execution on watcher instantiation
if (_.isEqual(next, prev)) return;
var doUpdate = false;
var doFetch = false;
var newFilters = [];
var oldFilters = [];
// iterate over each state type, checking for changes
stateWatchers.forEach(function (watcher, i) {
var nextVal = next[i];
var prevVal = prev[i];
newFilters = newFilters.concat(nextVal);
oldFilters = oldFilters.concat(prevVal);
// no update or fetch if there was no change
if (nextVal === prevVal) return;
if (nextVal) doUpdate = true;
// don't trigger fetch when only disabled filters
if (!onlyDisabled(nextVal, prevVal)) doFetch = true;
});
// make sure change wasn't only a state move
if (doFetch && newFilters.length === oldFilters.length) {
if (onlyStateChanged(newFilters, oldFilters)) doFetch = false;
}
// reconcile filter in global and app states
var filters = mergeAndMutateFilters(next[0], next[1]);
globalState.filters = filters[0];
var filters = mergeStateFilters(next[0], next[1]);
var globalFilters = filters[0];
var appFilters = filters[1];
var appState = getAppState();
if (appState) {
appState.filters = filters[1];
}
saveState();
// save the state, as it may have updated
var globalChanged = !_.isEqual(next[0], globalFilters);
var appChanged = !_.isEqual(next[1], appFilters);
// the filters were changed, apply to state (re-triggers this watcher)
if (globalChanged || appChanged) {
globalState.filters = globalFilters;
if (appState) appState.filters = appFilters;
return;
}
// check for actions, bail if we're done
getActions();
if (!doUpdate) return;
return queryFilter.emit('update')
// save states and emit the required events
saveState();
queryFilter.emit('update')
.then(function () {
if (!doFetch) return;
return queryFilter.emit('fetch');
queryFilter.emit('fetch');
});
// iterate over each state type, checking for changes
function getActions() {
var newFilters = [];
var oldFilters = [];
stateWatchers.forEach(function (watcher, i) {
var nextVal = next[i];
var prevVal = prev[i];
newFilters = newFilters.concat(nextVal);
oldFilters = oldFilters.concat(prevVal);
// no update or fetch if there was no change
if (nextVal === prevVal) return;
if (nextVal) doUpdate = true;
// don't trigger fetch when only disabled filters
if (!onlyDisabled(nextVal, prevVal)) doFetch = true;
});
// make sure change wasn't only a state move
// checking length first is an optimization
if (doFetch && newFilters.length === oldFilters.length) {
if (onlyStateChanged(newFilters, oldFilters)) doFetch = false;
}
}
});
}
}

View file

@ -71,7 +71,7 @@ define(function (require) {
newFilters.push(filter);
});
queryFilter.addFilters(newFilters);
return queryFilter.addFilters(newFilters);
};
return filterManager;

View file

@ -21,7 +21,7 @@ define(function (require) {
{ name: 'geo_shape', type: 'geo_shape', group: 'geo' },
{ name: 'ip', type: 'ip', group: 'other' },
{ name: 'attachment', type: 'attachment', group: 'other' },
{ name: 'murmur3', type: 'murmur3', group: 'hash' }
{ name: 'murmur3', type: 'murmur3', group: 'hash' }
]
});
@ -39,4 +39,4 @@ define(function (require) {
return castMappingType;
};
});
});

View file

@ -0,0 +1,23 @@
define(function (require) {
var parseInterval = require('utils/parse_interval');
require('modules')
.get('kibana')
.directive('validateDateInterval', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $el, attrs, ngModelCntrl) {
ngModelCntrl.$parsers.push(check);
ngModelCntrl.$formatters.push(check);
function check(value) {
ngModelCntrl.$setValidity('dateInterval', parseInterval(value) != null);
return value;
}
}
};
});
});

View file

@ -2,6 +2,7 @@ define(function (require) {
return function TooltipFactory(d3, Private) {
var _ = require('lodash');
var $ = require('jquery');
var positionTooltip = require('components/vislib/components/tooltip/_position_tooltip');
var allContents = [];
@ -32,42 +33,93 @@ define(function (require) {
this.showCondition = _.constant(true);
}
/**
* Get jquery reference to the tooltip node
*
* @return {Object} jQuery node object
*/
Tooltip.prototype.$get = _.once(function () {
return $('<div>').addClass(this.tooltipClass).appendTo(document.body);
});
/**
* Get jquery reference to the tooltip sizer node
*
* @return {Object} jQuery node object
*/
Tooltip.prototype.$getSizer = _.once(function () {
return this.$get()
.clone()
.removeClass(this.tooltipClass)
.addClass(this.tooltipSizerClass)
.appendTo(document.body);
.removeClass(this.tooltipClass)
.addClass(this.tooltipSizerClass)
.appendTo(document.body);
});
/**
* Calculates values for the tooltip placement
*
* @param event {Object} D3 Events Object
* @returns {{Object}} Coordinates for tooltip
* Show the tooltip, positioning it based on the content and chart container
*/
var positionTooltip = require('components/vislib/components/tooltip/_position_tooltip');
Tooltip.prototype.show = function () {
var $tooltip = this.$get();
var $chart = this.$getChart();
var html = $tooltip.html();
if (!$chart) return;
var placement = positionTooltip({
$window: $(window),
$chart: $chart,
$el: $tooltip,
$sizer: this.$getSizer(),
event: d3.event
}, html);
$tooltip.css({
visibility: 'visible',
left: placement.left,
top: placement.top
});
};
/**
* Hide the tooltip, clearing its contents
*/
Tooltip.prototype.hide = function () {
var $tooltip = this.$get();
allContents = [];
$tooltip.css({
visibility: 'hidden',
left: '-500px',
top: '-500px'
});
};
/**
* Get the jQuery chart node, based on the container object
* NOTE: the container is a d3 selection
*
* @return {Object} jQuery node for the chart
*/
Tooltip.prototype.$getChart = function () {
var chart = $(this.container && this.container.node());
return chart.size() ? chart : false;
};
/**
* Renders tooltip
*
* @method render
* @returns {Function} Renders tooltip on a D3 selection
* @return {Function} Renders tooltip on a D3 selection
*/
Tooltip.prototype.render = function () {
var self = this;
var tooltipFormatter = this.formatter;
var $window = $(window);
var $chart = $(this.el).find('.' + this.containerClass);
/**
* Calculates values for the tooltip placement
*
* @param {Object} selection D3 selection object
*/
return function (selection) {
var $tooltip = self.$get();
var $sizer = self.$getSizer();
var id = self.id;
var order = self.order;
@ -77,11 +129,14 @@ define(function (require) {
self.container = d3.select(self.el).select('.' + self.containerClass);
}
$chart.on('mouseleave', function (event) {
// only clear when we leave the chart, so that
// moving between points doesn't make it reposition
$chart.removeData('previousPlacement');
});
var $chart = self.$getChart();
if ($chart) {
$chart.on('mouseleave', function (event) {
// only clear when we leave the chart, so that
// moving between points doesn't make it reposition
$chart.removeData('previousPlacement');
});
}
selection.each(function (d, i) {
var element = d3.select(this);
@ -100,27 +155,10 @@ define(function (require) {
.join('\n');
if (allHtml) {
var placement = positionTooltip({
$window: $window,
$chart: $chart,
$el: $tooltip,
$sizer: $sizer,
event: d3.event
}, allHtml);
$tooltip
.html(allHtml)
.css({
visibility: 'visible',
left: placement.left,
top: placement.top
});
$tooltip.html(allHtml);
self.show();
} else {
$tooltip.css({
visibility: 'hidden',
left: '-500px',
top: '-500px'
});
self.hide();
}
}
@ -131,7 +169,7 @@ define(function (require) {
}
var events = self.events ? self.events.eventResponse(d, i) : d;
return render(tooltipFormatter(events));
return render(self.formatter(events));
})
.on('mouseleave.tip', function () {
render();

View file

@ -461,8 +461,7 @@ define(function (require) {
var self = this;
_.forEach(array, function (obj) {
var fieldFormatter = obj.aggConfig ? obj.aggConfig.fieldFormatter() : String;
names.push({ key: fieldFormatter(obj.name), index: index });
names.push({ key: obj.name, index: index });
if (obj.children) {
var plusIndex = index + 1;

View file

@ -52,8 +52,8 @@ define(function (require) {
*/
ResizeChecker.prototype.read = function () {
return {
w: this.$el.width(),
h: this.$el.height()
w: this.$el[0].clientWidth,
h: this.$el[0].clientHeight
};
};
@ -198,10 +198,10 @@ define(function (require) {
* @return {void}
*/
ResizeChecker.prototype.destroy = function () {
reflowWatcher.off('reflow', this.check);
reflowWatcher.off('reflow', this.onReflow);
clearTimeout(this._timerId);
};
return ResizeChecker;
};
});
});

View file

@ -82,6 +82,7 @@ define(function (require) {
Chart.prototype.destroy = function () {
var selection = d3.select(this.chartEl);
this.events.removeAllListeners();
if (this.tooltip) this.tooltip.hide();
selection.remove();
selection = null;
};

View file

@ -48,6 +48,7 @@ define(function (require) {
* @returns {D3.Selection}
*/
PointSeriesChart.prototype.createEndZones = function (svg) {
var self = this;
var xAxis = this.handler.xAxis;
var xScale = xAxis.xScale;
var yScale = xAxis.yScale;
@ -100,19 +101,22 @@ define(function (require) {
function callPlay(event) {
var boundData = event.target.__data__;
var mouseChartXCoord = event.clientX - self.chartEl.getBoundingClientRect().left;
var wholeBucket = boundData && boundData.x != null;
// the min and max that the endzones start in
var min = leftEndzone.w;
var max = rightEndzone.x;
// bounds of the cursor to consider
var xLeft = event.offsetX;
var xRight = event.offsetX;
var xLeft = mouseChartXCoord;
var xRight = mouseChartXCoord;
if (wholeBucket) {
xLeft = xScale(boundData.x);
xRight = xScale(xAxis.addInterval(boundData.x));
}
return {
wholeBucket: wholeBucket,
touchdown: min > xLeft || max < xRight
@ -133,4 +137,4 @@ define(function (require) {
return PointSeriesChart;
};
});
});

View file

@ -335,9 +335,9 @@ define(function (require) {
var line = svg.append('line')
.attr('class', 'base-line')
.attr('x1', 0)
.attr('y1', height)
.attr('y1', yScale(0))
.attr('x2', width)
.attr('y2', height)
.attr('y2', yScale(0))
.style('stroke', '#ddd')
.style('stroke-width', 1);

View file

@ -215,16 +215,7 @@ define(function (require) {
return yScale(d.y);
})
.attr('height', function (d) {
if (d.y < 0) {
return Math.abs(yScale(0) - yScale(d.y));
}
// if there is a negative yMin value, use yScale(0) instead of height
if (yMin < 0) {
return yScale(0) - yScale(d.y);
}
return height - yScale(d.y);
return Math.abs(yScale(0) - yScale(d.y));
});
return bars;
@ -315,25 +306,12 @@ define(function (require) {
var line = svg.append('line')
.attr('class', 'base-line')
.attr('x1', 0)
.attr('y1', height)
.attr('y1', yScale(0))
.attr('x2', width)
.attr('y2', height)
.attr('y2', yScale(0))
.style('stroke', '#ddd')
.style('stroke-width', 1);
if (yMin < 0) {
// Draw line at yScale 0 value
svg.append('line')
.attr('class', 'zero-line')
.attr('x1', 0)
.attr('y1', yScale(0))
.attr('x2', width)
.attr('y2', yScale(0))
.style('stroke', '#ddd')
.style('stroke-width', 1);
}
if (addTimeMarker) {
timeMarker.render(svg);
}

View file

@ -334,9 +334,9 @@ define(function (require) {
.append('line')
.attr('class', 'base-line')
.attr('x1', startLineX)
.attr('y1', height)
.attr('y1', yScale(0))
.attr('x2', width)
.attr('y2', height)
.attr('y2', yScale(0))
.style('stroke', '#ddd')
.style('stroke-width', lineStrokeWidth);

View file

@ -106,11 +106,6 @@ define(function (require) {
var tooltip = self.tooltip;
var isTooltip = self._attr.addTooltip;
var format = function (d, label) {
var formatter = d.aggConfig ? d.aggConfig.fieldFormatter() : String;
return formatter(label);
};
var partition = d3.layout.partition()
.sort(null)
.value(function (d) {
@ -155,7 +150,7 @@ define(function (require) {
.style('stroke', '#fff')
.style('fill', function (d) {
if (d.depth === 0) { return 'none'; }
return color(format(d, d.name));
return color(d.name);
});
if (isTooltip) {

View file

@ -6,7 +6,6 @@ define(function (require) {
require('leaflet-heat');
require('leaflet-draw');
var Dispatch = Private(require('components/vislib/lib/dispatch'));
var Chart = Private(require('components/vislib/visualizations/_chart'));
require('css!components/vislib/styles/main');
@ -14,6 +13,12 @@ define(function (require) {
var defaultMapCenter = [15, 5];
var defaultMapZoom = 2;
// Convenience function to turn around the LngLat recieved from ES
function cloneAndReverse(arr) {
var l = arr.length;
return arr.map(function (curr, idx) { return arr[l - (idx + 1)]; });
}
/**
* Tile Map Visualization: renders maps
*
@ -34,26 +39,24 @@ define(function (require) {
// track the map objects
this.maps = [];
this.tooltipFormatter = chartData.tooltipFormatter;
this.events = new Dispatch(handler);
this.originalConfig = chartData || {};
_.assign(this, this.originalConfig);
// add allmin and allmax to geoJson
var allMinMax = this.getMinMax(handler.data.data);
chartData.geoJson.properties.allmin = allMinMax.min;
chartData.geoJson.properties.allmax = allMinMax.max;
this.geoJson.properties.allmin = allMinMax.min;
this.geoJson.properties.allmax = allMinMax.max;
}
/**
* Renders tile map
*
* @method draw
* @param selection
* @return {Function} Creates the map
* @return {Function} - function to add a map to a selection
*/
TileMap.prototype.draw = function () {
var self = this;
var mapData = this.geoJson;
// clean up old maps
self.destroy();
@ -65,14 +68,12 @@ define(function (require) {
var worldBounds = L.latLngBounds([-90, -220], [90, 220]);
return function (selection) {
self._attr.mapZoom = self._attr.mapZoom || defaultMapZoom;
self._attr.mapCenter = self._attr.mapCenter || defaultMapCenter;
selection.each(function (data) {
selection.each(function () {
// add leaflet latLngs to properties for tooltip
var mapData = self.addLatLng(data.geoJson);
self.addLatLng(self.geoJson);
var div = $(this).addClass('tilemap');
var tileLayer = L.tileLayer('https://otile{s}-s.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpeg', {
@ -109,10 +110,9 @@ define(function (require) {
};
var map = L.map(div[0], mapOptions);
var featureLayer = self.markerType(map).addTo(map);
var featureLayer = self.markerType(map, mapData).addTo(map);
if (data.geoJson.features.length) {
if (mapData.features.length) {
map.addControl(new L.Control.Draw(drawOptions));
}
@ -132,7 +132,7 @@ define(function (require) {
map.removeLayer(featureLayer);
featureLayer = self.markerType(map, mapData).addTo(map);
featureLayer = self.markerType(map).addTo(map);
});
map.on('draw:created', function (e) {
@ -144,7 +144,7 @@ define(function (require) {
self.events.emit(drawType, {
e: e,
data: self.chartData,
chart: self.originalConfig,
bounds: {
top_left: {
lat: bounds.getNorthWest().lat,
@ -160,14 +160,14 @@ define(function (require) {
map.on('zoomend', function () {
self.events.emit('mapZoomEnd', {
data: mapData,
chart: self.originalConfig,
zoom: map.getZoom()
});
});
// add label for splits
if (mapData.properties.label) {
self.addLabel(mapData.properties.label, map);
// add title for splits
if (self.title) {
self.addTitle(self.title, map);
}
if (mapData && mapData.features.length > 0) {
@ -181,7 +181,7 @@ define(function (require) {
onAdd: function (map) {
$(fitContainer).html('<a class="leaflet-control-zoom fa fa-crop" title="Fit Data Bounds"></a>');
$(fitContainer).on('click', function () {
self.fitBounds(map, featureLayer);
self.fitBounds(map, mapData.features);
});
return fitContainer;
},
@ -208,7 +208,6 @@ define(function (require) {
* @return {boolean}
*/
TileMap.prototype._filterToMapBounds = function (map) {
function cloneAndReverse(arr) { return _(_.clone(arr)).reverse().value(); }
return function (feature) {
var mapBounds = map.getBounds();
var bucketRectBounds = feature.properties.rectangle.map(cloneAndReverse);
@ -250,20 +249,31 @@ define(function (require) {
return minMax;
};
/**
* Get the Rectangles representing the geohash grid
*
* @return {LatLngRectangles[]}
*/
TileMap.prototype._getDataRectangles = function () {
return _(this.geoJson.features)
.deepPluck('properties.rectangle')
.map(function (rectangle) { return rectangle.map(cloneAndReverse); })
.value();
};
/**
* add Leaflet latLng to mapData properties
*
* @method addLatLng
* @param mapData {geoJson Object}
* @return mapData {geoJson Object}
* @return undefined
*/
TileMap.prototype.addLatLng = function (mapData) {
for (var i = 0; i < mapData.features.length; i++) {
var latLng = L.latLng(mapData.features[i].geometry.coordinates[1], mapData.features[i].geometry.coordinates[0]);
mapData.features[i].properties.latLng = latLng;
}
return mapData;
TileMap.prototype.addLatLng = function () {
this.geoJson.features.forEach(function (feature) {
feature.properties.latLng = L.latLng(
feature.geometry.coordinates[1],
feature.geometry.coordinates[0]
);
});
};
/**
@ -271,18 +281,17 @@ define(function (require) {
*
* @method fitBounds
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {undefined}
* @return {boolean}
*/
TileMap.prototype.fitBounds = function (map, mapData) {
map.fitBounds(mapData._latlngs || mapData.getBounds());
TileMap.prototype.fitBounds = function (map) {
map.fitBounds(this._getDataRectangles());
};
/**
* remove css class for desat filters on map tiles
*
* @method saturateTiles
* @return {Leaflet object} featureLayer
* @return undefined
*/
TileMap.prototype.saturateTiles = function () {
if (!this._attr.isDesaturated) {
@ -295,11 +304,10 @@ define(function (require) {
*
* @method nearestFeature
* @param point {Leaflet Object}
* @param mapData {geoJson Object}
* @return nearestPoint {Leaflet Object}
*/
TileMap.prototype.nearestFeature = function (point, mapData) {
var self = this;
TileMap.prototype.nearestFeature = function (point) {
var mapData = this.geoJson;
var distance = Infinity;
var nearest;
@ -330,9 +338,7 @@ define(function (require) {
* @return boolean
*/
TileMap.prototype.tooltipProximity = function (latlng, zoom, feature, map) {
if (!feature) {
return;
}
if (!feature) return;
var showTip = false;
@ -370,16 +376,15 @@ define(function (require) {
* features and shows tooltip for that feature
*
* @method showTooltip
* @param e {Event}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {undefined}
* @param map {LeafletMap}
* @param feature {LeafletFeature}
* @return undefined
*/
TileMap.prototype.showTooltip = function (map, feature) {
if (!this.tooltipFormatter) return;
var content = this.tooltipFormatter(feature);
if (!content) {
return;
}
if (!content) return;
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
@ -400,24 +405,24 @@ define(function (require) {
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.markerType = function (map, mapData) {
var featureLayer;
if (mapData) {
if (this._attr.mapType === 'Scaled Circle Markers') {
featureLayer = this.scaledCircleMarkers(map, mapData);
} else if (this._attr.mapType === 'Shaded Circle Markers') {
featureLayer = this.shadedCircleMarkers(map, mapData);
} else if (this._attr.mapType === 'Shaded Geohash Grid') {
featureLayer = this.shadedGeohashGrid(map, mapData);
} else if (this._attr.mapType === 'Heatmap') {
featureLayer = this.heatMap(map, mapData);
} else {
featureLayer = this.scaledCircleMarkers(map, mapData);
}
TileMap.prototype.markerType = function (map) {
if (this._attr.mapType === 'Scaled Circle Markers') {
return this.scaledCircleMarkers(map);
}
return featureLayer;
if (this._attr.mapType === 'Heatmap') {
return this.heatMap(map);
}
if (this._attr.mapType === 'Shaded Circle Markers') {
return this.shadedCircleMarkers(map);
}
if (this._attr.mapType === 'Shaded Geohash Grid') {
return this.shadedGeohashGrid(map);
}
return this.scaledCircleMarkers(map);
};
/**
@ -430,14 +435,17 @@ define(function (require) {
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.scaledCircleMarkers = function (map, mapData) {
TileMap.prototype.scaledCircleMarkers = function (map) {
var self = this;
var mapData = self.geoJson;
// super min and max from all chart data
var min = mapData.properties.allmin;
var max = mapData.properties.allmax;
var zoom = map.getZoom();
var precision = mapData.properties.precision;
var precision = _.max(mapData.features.map(function (feature) {
return String(feature.properties.geohash).length;
}));
// multiplier to reduce size of all circles
var scaleFactor = 0.6;
@ -446,8 +454,8 @@ define(function (require) {
var featureLayer = L.geoJson(mapData, {
pointToLayer: function (feature, latlng) {
var count = feature.properties.count;
var scaledRadius = self.radiusScale(count, max, zoom, precision) * scaleFactor;
var value = feature.properties.value;
var scaledRadius = self.radiusScale(value, max, zoom, precision) * scaleFactor;
return L.circleMarker(latlng).setRadius(scaledRadius);
},
onEachFeature: function (feature, layer) {
@ -459,10 +467,7 @@ define(function (require) {
filter: self._filterToMapBounds(map)
});
// add legend
if (mapData.features.length > 1) {
self.addLegend(mapData, map);
}
self.addLegend(map);
return featureLayer;
};
@ -477,9 +482,9 @@ define(function (require) {
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.shadedCircleMarkers = function (map, mapData) {
TileMap.prototype.shadedCircleMarkers = function (map) {
var self = this;
var mapData = self.geoJson;
// super min and max from all chart data
var min = mapData.properties.allmin;
var max = mapData.properties.allmax;
@ -489,7 +494,6 @@ define(function (require) {
var featureLayer = L.geoJson(mapData, {
pointToLayer: function (feature, latlng) {
var count = feature.properties.count;
var radius = self.geohashMinDistance(feature) * scaleFactor;
return L.circle(latlng, radius);
},
@ -502,10 +506,7 @@ define(function (require) {
filter: self._filterToMapBounds(map)
});
// add legend
if (mapData.features.length > 1) {
self.addLegend(mapData, map);
}
self.addLegend(map);
return featureLayer;
};
@ -520,8 +521,9 @@ define(function (require) {
* @param mapData {geoJson Object}
* @return {undefined}
*/
TileMap.prototype.shadedGeohashGrid = function (map, mapData) {
TileMap.prototype.shadedGeohashGrid = function (map) {
var self = this;
var mapData = self.geoJson;
// super min and max from all chart data
var min = mapData.properties.allmin;
@ -558,10 +560,7 @@ define(function (require) {
filter: self._filterToMapBounds(map)
});
// add legend
if (mapData.features.length > 1) {
self.addLegend(mapData, map);
}
self.addLegend(map);
return featureLayer;
};
@ -576,10 +575,10 @@ define(function (require) {
* @param mapData {geoJson Object}
* @return featureLayer {Leaflet object}
*/
TileMap.prototype.heatMap = function (map, mapData) {
TileMap.prototype.heatMap = function (map) {
var self = this;
var max = mapData.properties.allmax;
var points = this.dataToHeatArray(mapData, max);
var mapData = this.geoJson;
var points = this.dataToHeatArray(mapData.properties.allmax);
var options = {
radius: +this._attr.heatRadius,
@ -620,7 +619,7 @@ define(function (require) {
var latlng = e.latlng;
// find nearest feature to event latlng
var feature = self.nearestFeature(latlng, mapData);
var feature = self.nearestFeature(latlng);
var zoom = map.getZoom();
@ -636,12 +635,12 @@ define(function (require) {
/**
* Adds label div to each map when data is split
*
* @method addLabel
* @method addTitle
* @param mapLabel {String}
* @param map {Leaflet Object}
* @return {undefined}
*/
TileMap.prototype.addLabel = function (mapLabel, map) {
TileMap.prototype.addTitle = function (mapLabel, map) {
var label = L.control();
label.onAdd = function () {
this._div = L.DomUtil.create('div', 'tilemap-info tilemap-label');
@ -659,46 +658,39 @@ define(function (require) {
* uses d3 scale from TileMap.prototype.quantizeColorScale
*
* @method addLegend
* @param data {Object}
* @param map {Leaflet Object}
* @return {undefined}
*/
TileMap.prototype.addLegend = function (data, map) {
TileMap.prototype.addLegend = function (map) {
// only draw the legend for maps with multiple items
if (this.geoJson.features.length <= 1) return;
var self = this;
var isLegend = $('div.tilemap-legend', this.chartEl).length;
if (isLegend) return; // Don't add Legend if already one
var valueFormatter = this.valueFormatter || _.identity;
var legend = L.control({position: 'bottomright'});
legend.onAdd = function () {
var div = L.DomUtil.create('div', 'tilemap-legend');
var colors = self._attr.colors;
var labels = [];
var i = 0;
var vals;
var strokecol;
var $div = $('<div>').addClass('tilemap-legend');
if (data.properties.min === data.properties.max) {
// 1 val for legend
vals = self._attr.cScale.invertExtent(colors[i]);
strokecol = self.darkerColor(colors[i]);
labels.push(
'<i style="background:' + colors[i] + ';border-color:' + strokecol + '"></i> ' +
vals[0].toFixed(0));
} else {
// 3 to 5 vals for legend
if (colors) {
for (i = 0; i < colors.length; i++) {
vals = self._attr.cScale.invertExtent(colors[i]);
strokecol = self.darkerColor(colors[i]);
labels.push('<i style="background:' + colors[i] + ';border-color:' +
strokecol + '"></i> ' + vals[0].toFixed(0) + ' &ndash; ' + vals[1].toFixed(0));
}
}
}
div.innerHTML = labels.join('<br>');
_.each(self._attr.colors, function (color, i) {
var icon = $('<i>').css({
background: color,
'border-color': self.darkerColor(color)
});
return div;
var range = self._attr.cScale
.invertExtent(color)
.map(valueFormatter)
.join('  ');
$div.append(i > 0 ? '<br>' : '').append(icon).append(range);
});
return $div.get(0);
};
legend.addTo(map);
};
@ -714,8 +706,8 @@ define(function (require) {
*/
TileMap.prototype.applyShadingStyle = function (feature, min, max) {
var self = this;
var count = feature.properties.count;
var color = self.quantizeColorScale(count, min, max);
var value = feature.properties.value;
var color = self.quantizeColorScale(value, min, max);
return {
fillColor: color,
@ -779,8 +771,9 @@ define(function (require) {
* @method dataToHeatArray
* @return {Array}
*/
TileMap.prototype.dataToHeatArray = function (mapData, max) {
TileMap.prototype.dataToHeatArray = function (max) {
var self = this;
var mapData = this.geoJson;
return mapData.features.map(function (feature) {
var lat = feature.geometry.coordinates[1];
@ -788,11 +781,11 @@ define(function (require) {
var heatIntensity;
if (!self._attr.heatNormalizeData) {
// show bucket count on heatmap
heatIntensity = feature.properties.count;
// show bucket value on heatmap
heatIntensity = feature.properties.value;
} else {
// show bucket count normalized to max value
heatIntensity = parseInt(feature.properties.count / max * 100);
// show bucket value normalized to max value
heatIntensity = parseInt(feature.properties.value / max * 100);
}
return [lat, lng, heatIntensity];
@ -826,29 +819,25 @@ define(function (require) {
/**
* radiusScale returns a number for scaled circle markers
* square root of count / max
* square root of value / max
* multiplied by a value based on map zoom
* multiplied by a value based on data precision
* for relative sizing of markers
*
* @method radiusScale
* @param count {Number}
* @param value {Number}
* @param max {Number}
<<<<<<< HEAD
* @param feature {Object}
=======
* @param zoom {Number}
* @param precision {Number}
>>>>>>> f0414d554915d151e2cdc3501bd3c7fd1889a0a8
* @return {Number}
*/
TileMap.prototype.radiusScale = function (count, max, zoom, precision) {
TileMap.prototype.radiusScale = function (value, max, zoom, precision) {
// exp = 0.5 for square root ratio
// exp = 1 for linear ratio
var exp = 0.5;
var precisionBiasNumerator = 200;
var precisionBiasBase = 5;
var pct = count / max;
var pct = value / max;
var constantZoomRadius = 0.5 * Math.pow(2, zoom);
var precisionScale = precisionBiasNumerator / Math.pow(precisionBiasBase, precision);
@ -860,12 +849,12 @@ define(function (require) {
* used for marker fill color
*
* @method quantizeColorScale
* @param count {Number}
* @param value {Number}
* @param min {Number}
* @param max {Number}
* @return {String} hex color
*/
TileMap.prototype.quantizeColorScale = function (count, min, max) {
TileMap.prototype.quantizeColorScale = function (value, min, max) {
var reds5 = ['#fed976', '#feb24c', '#fd8d3c', '#f03b20', '#bd0026'];
var reds3 = ['#fecc5c', '#fd8d3c', '#e31a1c'];
var reds1 = ['#ff6128'];
@ -884,7 +873,7 @@ define(function (require) {
if (max === min) {
return colors[0];
} else {
return cScale(count);
return cScale(value);
}
};

View file

@ -1,7 +1,7 @@
<form role="form">
<div class="form-group finder-form">
<div>
<a class="small pull-right" ng-click="manageObject(type)">manage {{type}}</a>
<div class="finder-form-options">
<a class="small" ng-click="manageObject(type)">manage {{type}}</a>
</div>
<div class="clearfix visible-xs-block"></div>
<input

View file

@ -12,25 +12,14 @@ define(function (require) {
function SavedDashboard(id) {
// Gives our SavedDashboard the properties of a SavedObject
courier.SavedObject.call(this, {
// this object will be saved at {{configFile.kibana_index}}/dashboard/{{id}}
type: SavedDashboard.type,
mapping: SavedDashboard.mapping,
searchSource: SavedDashboard.searchsource,
// if this is null/undefined then the SavedObject will be assigned the defaults
id: id,
// if type:dashboard has no mapping, we push this mapping into ES
mapping: {
title: 'string',
hits: 'integer',
description: 'string',
panelsJSON: 'string',
version: 'integer',
timeRestore: 'boolean',
timeTo: 'string',
timeFrom: 'string'
},
// defeult values to assign to the doc
// default values that will get assigned if the doc is new
defaults: {
title: 'New Dashboard',
hits: 0,
@ -42,14 +31,29 @@ define(function (require) {
timeFrom: undefined
},
searchSource: true,
// if an indexPattern was saved with the searchsource of a SavedDashboard
// object, clear it. It was a mistake
clearSavedIndexPattern: true
});
}
// save these objects with the 'dashboard' type
SavedDashboard.type = 'dashboard';
// if type:dashboard has no mapping, we push this mapping into ES
SavedDashboard.mapping = {
title: 'string',
hits: 'integer',
description: 'string',
panelsJSON: 'string',
version: 'integer',
timeRestore: 'boolean',
timeTo: 'string',
timeFrom: 'string'
};
SavedDashboard.searchsource = true;
return SavedDashboard;
});
});

View file

@ -15,6 +15,7 @@ define(function (require) {
// This is the only thing that gets injected into controllers
module.service('savedDashboards', function (Promise, SavedDashboard, config, es, kbnUrl) {
this.type = SavedDashboard.type;
this.Class = SavedDashboard;
// Returns a single dashboard by ID, should be the name of the dashboard
this.get = function (id) {

View file

@ -169,6 +169,11 @@ define(function (require) {
});
});
// update data source when hitting forward/back and the query changes
$scope.$listen($state, 'fetch_with_changes', function (diff) {
if (diff.indexOf('query') >= 0) $scope.fetch();
});
// fetch data when filters fire fetch event
$scope.$listen(queryFilter, 'fetch', $scope.fetch);

View file

@ -78,7 +78,7 @@
<div class="discover-info">
<span ng-show="opts.savedSearch.id" class="discover-info-title">
<span bindonce bo-bind="opts.savedSearch.title"></span>
<i aria-label="Reload saved query" tooltip="Reload saved query" ng-click="resetQuery();" class="fa fa-undo small"></i>
<i aria-label="Reload Saved Search" tooltip="Reload Saved Search" ng-click="resetQuery();" class="fa fa-undo small"></i>
</span>
<strong class="discover-info-hits">{{(hits || 0) | number:0}}</strong>

View file

@ -13,18 +13,10 @@ define(function (require) {
function SavedSearch(id) {
courier.SavedObject.call(this, {
type: SavedSearch.type,
mapping: SavedSearch.mapping,
searchSource: SavedSearch.searchSource,
id: id,
mapping: {
title: 'string',
description: 'string',
hits: 'integer',
columns: 'string',
sort: 'string',
version: 'integer'
},
defaults: {
title: 'New Saved Search',
description: '',
@ -32,14 +24,23 @@ define(function (require) {
hits: 0,
sort: [],
version: 1
},
searchSource: true
}
});
}
SavedSearch.type = 'search';
SavedSearch.mapping = {
title: 'string',
description: 'string',
hits: 'integer',
columns: 'string',
sort: 'string',
version: 'integer'
};
SavedSearch.searchSource = true;
return SavedSearch;
});
});
});

View file

@ -23,6 +23,7 @@ define(function (require) {
});
this.type = SavedSearch.type;
this.Class = SavedSearch;
this.get = function (id) {
return (new SavedSearch(id)).init();

View file

@ -28,7 +28,11 @@ define(function (require) {
var Notifier = require('components/notify/_notifier');
// ensure that the kibana module requires ui.bootstrap
require('modules').get('kibana', ['ui.bootstrap'])
require('modules')
.get('kibana', ['ui.bootstrap'])
.config(function ($tooltipProvider) {
$tooltipProvider.setTriggers({ 'mouseenter': 'mouseleave click' });
})
.directive('kibana', function (Private, $rootScope, $injector, Promise, config, kbnSetup) {
return {
template: require('text!plugins/kibana/kibana.html'),

View file

@ -5,3 +5,41 @@
padding: 1em;
width: 100%;
}
.vis-editor {
&.vis-type-markdown {
.vis-editor-config {
.flex-parent();
> ul {
.flex-parent();
.flex(1, 1, auto);
li {
.flex-parent();
.flex(1, 1, auto);
}
}
}
.visualization-options {
.flex-parent();
.flex(1, 1, auto);
}
.markdown-vis-options {
.flex-parent();
.flex(1, 1, auto);
textarea {
.flex(1, 1, auto);
resize: none;
}
}
}
}

View file

@ -1,5 +1,7 @@
<div class="markdown-vis-options form-group">
<label>Markdown</label>
<small class="pull-right"><a target="_window" href="https://help.github.com/articles/github-flavored-markdown/">Help <i aria-hidden="true" class="fa fa-link"></i></a></small>
<div>
<label>Markdown</label>
<small class="pull-right"><a target="_window" href="https://help.github.com/articles/github-flavored-markdown/">Help <i aria-hidden="true" class="fa fa-link"></i></a></small>
</div>
<textarea ng-model="vis.params.markdown" class="form-control" rows="20"></textarea>
</div>

View file

@ -15,9 +15,9 @@ define(function (require) {
.directive('kbnSettingsObjectsView', function (config, Notifier) {
return {
restrict: 'E',
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, es) {
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, es, Private) {
var notify = new Notifier({ location: 'SavedObject view' });
var castMappingType = Private(require('components/index_patterns/_cast_mapping_type'));
var serviceObj = registry.get($routeParams.service);
var service = $injector.get(serviceObj.service);
@ -71,14 +71,50 @@ define(function (require) {
return memo;
};
var readObjectClass = function (fields, Class) {
var fieldMap = _.indexBy(fields, 'name');
_.forOwn(Class.mapping, function (esType, name) {
if (fieldMap[name]) return;
fields.push({
name: name,
type: (function () {
switch (castMappingType(esType)) {
case 'string': return 'text';
case 'number': return 'number';
case 'boolean': return 'boolean';
default: return 'json';
}
}())
});
});
if (Class.searchSource && !fieldMap['kibanaSavedObjectMeta.searchSourceJSON']) {
fields.push({
name: 'kibanaSavedObjectMeta.searchSourceJSON',
type: 'json',
value: '{}'
});
}
};
$scope.notFound = $routeParams.notFound;
$scope.title = inflection.singularize(serviceObj.title);
service.get($routeParams.id).then(function (obj) {
es.get({
index: config.file.kibana_index,
type: service.type,
id: $routeParams.id
})
.then(function (obj) {
$scope.obj = obj;
$scope.link = service.urlFor(obj.id);
$scope.fields = _.reduce(_.defaults(obj.serialize(), obj.defaults), createField, []);
$scope.link = service.urlFor(obj._id);
var fields = _.reduce(obj._source, createField, []);
if (service.Class) readObjectClass(fields, service.Class);
$scope.fields = _.sortBy(fields, 'name');
})
.catch(notify.fatal);

View file

@ -27,19 +27,19 @@ define(function (require) {
},
listeners: {
rectangle: function (event) {
var agg = _.deepGet(event, 'data.geoJson.properties.agg');
var agg = _.deepGet(event, 'chart.geohashGridAgg');
if (!agg) return;
var pushFilter = Private(require('components/filter_bar/push_filter'))(getAppState());
var indexPatternName = agg.geo.vis.indexPattern.id;
var field = agg.geo.fieldName();
var indexPatternName = agg.vis.indexPattern.id;
var field = agg.fieldName();
var filter = {geo_bounding_box: {}};
filter.geo_bounding_box[field] = event.bounds;
pushFilter(filter, false, indexPatternName);
},
mapZoomEnd: function (event) {
var agg = _.deepGet(event, 'data.properties.agg.geo');
var agg = _.deepGet(event, 'chart.geohashGridAgg');
if (!agg || !agg.params.autoPrecision) return;
// zoomPrecision maps event.zoom to a geohash precision value

View file

@ -21,9 +21,12 @@ define(function (require) {
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);
}
$scope.$watch('editorOpen', function (open) {
// make sure that all of the form inputs are "touched"
// so that their errors propogate
if (!open) kbnForm.$setTouched();
});
$scope.$watchMulti([
'$index',

View file

@ -159,13 +159,6 @@ define(function (require) {
initialSet: fields
});
}
// bind a property from our scope a child scope, with one-way binding
function setupBoundProp($child, get, set) {
var getter = _.partial($parse(get), $scope);
var setter = _.partial($parse(set).assign, $child);
$scope.$watch(getter, setter);
}
}
};
});

View file

@ -1,4 +1,4 @@
<div ng-controller="VisEditor" class="vis-editor">
<div ng-controller="VisEditor" class="vis-editor vis-type-{{ vis.type.name }}">
<navbar ng-if="!appEmbedded">
<div class="fill bitty-modal-container">

View file

@ -170,6 +170,8 @@ define(function (require) {
}
if (_.contains(keys, 'vis')) {
$state.vis.listeners = _.defaults($state.vis.listeners || {}, vis.listeners);
// only update when we need to, otherwise colors change and we
// risk loosing an in-progress result
vis.setState($state.vis);

View file

@ -17,17 +17,11 @@ define(function (require) {
SavedVis.Super.call(self, {
type: SavedVis.type,
mapping: SavedVis.mapping,
searchSource: SavedVis.searchSource,
id: opts.id,
mapping: {
title: 'string',
visState: 'json',
description: 'string',
savedSearchId: 'string',
version: 'integer'
},
indexPattern: opts.indexPattern,
defaults: {
title: 'New Visualization',
visState: (function () {
@ -41,15 +35,22 @@ define(function (require) {
version: 1
},
searchSource: true,
indexPattern: opts.indexPattern,
afterESResp: this._afterEsResp
});
}
SavedVis.type = 'visualization';
SavedVis.mapping = {
title: 'string',
visState: 'json',
description: 'string',
savedSearchId: 'string',
version: 'integer'
};
SavedVis.searchSource = true;
SavedVis.prototype._afterEsResp = function () {
var self = this;

View file

@ -18,6 +18,7 @@ define(function (require) {
});
this.type = SavedVis.type;
this.Class = SavedVis;
this.get = function (id) {
return (new SavedVis(id)).init();

View file

@ -12,7 +12,7 @@
<i aria-hidden="true" class="fa fa-fw" ng-class="type.icon"></i>
<h4>{{type.title}}</h4>
</div>
<span class="wizard-vis-type-description">{{type.description}}</span>
<p class="wizard-vis-type-description">{{type.description}}</p>
</a>
</div>

View file

@ -102,7 +102,8 @@ ul.navbar-inline li {
opacity: 90%;
&-inner {
word-break: break-word;
word-break: normal;
word-wrap: break-word;
white-space: normal;
}
}
@ -350,6 +351,12 @@ saved-object-finder {
div.finder-form {
position: relative;
&-options {
.flex-parent();
.flex-direction(row);
.justify-content(flex-end);
}
}
span.finder-hit-count {

View file

@ -3,20 +3,19 @@ define(function (require) {
var moment = require('moment');
var datemath = require('utils/datemath');
return function parseInterval(interval) {
// Assume interval is in the form (value)(unit), such as "1h"
var regex = new RegExp('^([0-9\\.]*)\\s*(' + datemath.units.join('|') + ')$');
var matches = regex.exec(interval);
var value;
var unit;
// Assume interval is in the form (value)(unit), such as "1h"
var INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + datemath.units.join('|') + ')$');
if (matches && matches.length) {
value = parseFloat(matches[1]) || 1;
unit = matches[2];
}
return function parseInterval(interval) {
var matches = String(interval).trim().match(INTERVAL_STRING_RE);
if (!matches) return null;
try {
interval = moment.duration(value, unit);
var value = parseFloat(matches[1]) || 1;
var unit = matches[2];
var duration = moment.duration(value, unit);
// There is an error with moment, where if you have a fractional interval between 0 and 1, then when you add that
// interval to an existing moment object, it will remain unchanged, which causes problems in the ordered_x_keys
@ -26,9 +25,10 @@ define(function (require) {
// the start date, you get the same exact date (instead of being ahead by 12 hours). So instead of returning
// a duration corresponding to 0.5 hours, we return a duration corresponding to 12 hours.
var selectedUnit = _.find(datemath.units, function (unit) {
return Math.abs(interval.as(unit)) >= 1;
return Math.abs(duration.as(unit)) >= 1;
});
return moment.duration(interval.as(selectedUnit), selectedUnit);
return moment.duration(duration.as(selectedUnit), selectedUnit);
} catch (e) {
return null;
}

View file

@ -1,5 +1,5 @@
module.exports = function (grunt) {
var notIncludedComponents = '{font-awesome,requirejs,zeroclipboard}';
var notIncludedComponents = '{font-awesome,requirejs,zeroclipboard,leaflet-draw}';
return {
build: '<%= build %>',
target: '<%= target %>',
@ -16,6 +16,7 @@ module.exports = function (grunt) {
'!<%= build %>/kibana/public/bower_components/requirejs/require.js',
'!<%= build %>/kibana/public/bower_components/font-awesome/fonts',
'!<%= build %>/kibana/public/bower_components/zeroclipboard/dist',
'!<%= build %>/kibana/public/bower_components/leaflet-draw/dist',
// delete the contents of the dist dir, except the ZeroClipboard.swf file
'<%= build %>/kibana/public/bower_components/zeroclipboard/dist/*',

View file

@ -71,6 +71,12 @@ module.exports = function (grunt) {
cwd: '<%= server %>/config/',
src: 'kibana.yml',
dest: '<%= build %>/dist/kibana/config/'
},
{
expand: true,
cwd: '<%= bowerComponentsDir %>/ace-builds/src-noconflict/',
src: 'worker-json.js',
dest: '<%= build %>/dist/kibana/src/public/'
}
]
},

View file

@ -43,17 +43,19 @@ function createPackages(grunt) {
commands.push([ 'zip', '-rq', archiveName + '.zip', name ]);
}
// TODO(sissel): Add before-install scripts to create kibana user
// TODO(sissel): Check if `fpm` is available
if (/linux-x(86|64)$/.test(name)) {
// kibana.rpm and kibana.deb
var sysv_init = join(distPath, 'services', 'sysv') + '/etc/=/etc/';
commands.push(fpm_options.concat(['-t', 'rpm', '-a', arch, '--rpm-os', 'linux', fpm_files, sysv_init]));
commands.push(fpm_options.concat(['-t', 'deb', '-a', arch, fpm_files, sysv_init]));
} else if (/darwin-x(86|64)$/.test(name)) {
// kibana.pkg
var launchd = join(distPath, 'services', 'launchd') + '/=/';
commands.push(fpm_options.concat(['-t', 'osxpkg', '-a', arch, fpm_files, launchd]));
if (grunt.option('os-packages')) {
// TODO(sissel): Add before-install scripts to create kibana user
// TODO(sissel): Check if `fpm` is available
if (/linux-x(86|64)$/.test(name)) {
// kibana.rpm and kibana.deb
var sysv_init = join(distPath, 'services', 'sysv') + '/etc/=/etc/';
commands.push(fpm_options.concat(['-t', 'rpm', '-a', arch, '--rpm-os', 'linux', fpm_files, sysv_init]));
commands.push(fpm_options.concat(['-t', 'deb', '-a', arch, fpm_files, sysv_init]));
} else if (/darwin-x(86|64)$/.test(name)) {
// kibana.pkg
var launchd = join(distPath, 'services', 'launchd') + '/=/';
commands.push(fpm_options.concat(['-t', 'osxpkg', '-a', arch, fpm_files, launchd]));
}
}
return mkdirp.mkdirpAsync(target)

View file

@ -0,0 +1,83 @@
define(function (require) {
return function GeoHashGridAggResponseFixture() {
var _ = require('lodash');
// for vis:
//
// vis = new Vis(indexPattern, {
// type: 'tile_map',
// aggs:[
// { schema: 'metric', type: 'avg', params: { field: 'bytes' } },
// { schema: 'split', type: 'terms', params: { field: '@tags', size: 10 } },
// { schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3 } }
// ],
// params: {
// isDesaturated: true,
// mapType: 'Scaled%20Circle%20Markers'
// },
// });
var geoHashCharts = _.union(
_.range(48, 57), // 0-9
_.range(65, 90), // A-Z
_.range(97, 122) // a-z
);
var totalDocCount = 0;
var tags = _.times(_.random(4, 20), function (i) {
// random number of tags
var docCount = 0;
var buckets = _.times(_.random(40, 200), function () {
return _.sample(geoHashCharts, 3).join('');
})
.sort()
.map(function (geoHash) {
var count = _.random(1, 5000);
totalDocCount += count;
docCount += count;
return {
key: geoHash,
doc_count: count,
1: {
value: 2048 + i
}
};
});
return {
key: 'tag ' + (i + 1),
doc_count: docCount,
3: {
buckets: buckets
},
1: {
value: 1000 + i
}
};
});
return {
took: 3,
timed_out: false,
_shards: {
total: 4,
successful: 4,
failed: 0
},
hits: {
total: 298,
max_score: 0.0,
hits: []
},
aggregations: {
2: {
buckets: tags
}
}
};
};
});

View file

@ -13,6 +13,7 @@ define(function (require) {
{ name: 'point', type: 'geo_point', indexed: true, analyzed: true, sortable: false, filterable: false },
{ name: 'area', type: 'geo_shape', indexed: true, analyzed: true, sortable: true, filterable: false },
{ name: 'hashed', type: 'murmur3', indexed: true, analyzed: true, sortable: false, filterable: false },
{ name: 'geo.coordinates', type: 'geo_point', indexed: true, analyzed: true, sortable: false, filterable: true },
{ name: 'extension', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'machine.os', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'geo.src', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
@ -20,8 +21,8 @@ define(function (require) {
{ name: '_id', type: 'string', indexed: false, analyzed: false, sortable: false, filterable: true},
{ name: '_source', type: 'string', indexed: false, analyzed: false, sortable: false, filterable: false},
{ name: 'custom_user_field', type: 'conflict', indexed: false, analyzed: false, sortable: false, filterable: true },
{ name: 'script string', type: 'string', scripted: true, script: '\'i am a string\'', lang: 'expression'},
{ name: 'script number', type: 'number', scripted: true, script: '1234', lang: 'expression'},
{ name: 'script string', type: 'string', scripted: true, script: '\'i am a string\'', lang: 'expression' },
{ name: 'script number', type: 'number', scripted: true, script: '1234', lang: 'expression' },
].map(function (field) {
field.count = field.count || 0;
field.scripted = field.scripted || false;
@ -32,4 +33,4 @@ define(function (require) {
}
return stubbedLogstashFields;
});
});

View file

@ -0,0 +1,17 @@
define(function (require) {
var _ = require('lodash');
var sinon = require('test_utils/auto_release_sinon');
return function (Private, Promise) {
var indexPatterns = Private(require('fixtures/stubbed_logstash_index_pattern'));
var getIndexPatternStub = sinon.stub();
getIndexPatternStub.returns(Promise.resolve(indexPatterns));
var courier = {
indexPatterns: { get: getIndexPatternStub },
getStub: getIndexPatternStub
};
return courier;
};
});

View file

@ -13,6 +13,27 @@
var SAUCELABS = !!(/saucelabs/i.test(window.location.search));
var DISABLE_RESIZE_CHECKER = true;
var mochaSuiteOn = mocha.suite.on;
mocha.suite.on = function (eventName, fn) {
if (eventName !== 'pre-require') {
return mochaSuiteOn.call(this, eventName, fn);
}
return mochaSuiteOn.call(this, eventName, function (context, file, mocha) {
fn.call(this, context, file, mocha);
var contextDescribe = context.describe;
context.describe = function (name, body) {
if (!body && name && name instanceof Array) {
contextDescribe(name[0], name[1]);
} else {
contextDescribe(name, body);
}
};
});
};
mocha.setup({
ui: 'bdd',
reporter: 'html'
@ -144,6 +165,7 @@
});
}
if (COVERAGE) {
setupCoverage(runTests);
}

View file

@ -0,0 +1,153 @@
define(function (require) {
var _ = require('lodash');
describe('GeoJson Agg Response Converter', function () {
var vis;
var tabify;
var convert;
var esResponse;
var aggs;
beforeEach(module('kibana'));
beforeEach(inject(function (Private) {
var Vis = Private(require('components/vis/vis'));
var indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
esResponse = Private(require('fixtures/agg_resp/geohash_grid'));
tabify = Private(require('components/agg_response/tabify/tabify'));
convert = Private(require('components/agg_response/geo_json/geo_json'));
vis = new Vis(indexPattern, {
type: 'tile_map',
aggs: [
{ schema: 'metric', type: 'avg', params: { field: 'bytes' } },
{ schema: 'split', type: 'terms', params: { field: '@tags' } },
{ schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3 } }
],
params: {
isDesaturated: true,
mapType: 'Scaled%20Circle%20Markers'
}
});
aggs = {
metric: vis.aggs[0],
split: vis.aggs[1],
geo: vis.aggs[2]
};
}));
[ { asAggConfigResults: true }, { asAggConfigResults: false } ].forEach(function (tableOpts) {
function makeTable() {
return _.sample(_.sample(tabify(vis, esResponse, tableOpts).tables).tables);
}
function makeSingleChart(table) {
return convert(vis, table || makeTable(), tableOpts);
}
function makeGeoJson() {
return makeSingleChart().geoJson;
}
describe('with table ' + JSON.stringify(tableOpts), function () {
it('outputs a chart', function () {
var table = makeTable();
var chart = makeSingleChart(table);
expect(chart).to.only.have.keys(
'title',
'tooltipFormatter',
'valueFormatter',
'geohashGridAgg',
'geoJson'
);
expect(chart.title).to.be(table.title());
expect(chart.tooltipFormatter).to.be.a('function');
expect(chart.valueFormatter).to.be(aggs.metric.fieldFormatter());
expect(chart.geohashGridAgg).to.be(aggs.geo);
expect(chart.geoJson).to.be.an('object');
});
it('outputs geohash points as features in a feature collection', function () {
var table = makeTable();
var chart = makeSingleChart(table);
var geoJson = chart.geoJson;
expect(geoJson.type).to.be('FeatureCollection');
expect(geoJson.features).to.be.an('array');
expect(geoJson.features).to.have.length(table.rows.length);
});
it('exports a bunch of properties about the geo hash grid', function () {
var geoJson = makeGeoJson();
var props = geoJson.properties;
// props
expect(props).to.be.an('object');
expect(props).to.only.have.keys('min', 'max');
// props.min
expect(props.min).to.be.a('number');
expect(props.min).to.be.greaterThan(0);
// props.max
expect(props.max).to.be.a('number');
expect(props.max).to.be.greaterThan(0);
});
describe('properties', function () {
it('includes one feature per row in the table', function () {
var table = makeTable();
var chart = makeSingleChart(table);
var geoColI = _.findIndex(table.columns, { aggConfig: aggs.geo });
var metricColI = _.findIndex(table.columns, { aggConfig: aggs.metric });
table.rows.forEach(function (row, i) {
var feature = chart.geoJson.features[i];
expect(feature).to.have.property('geometry');
expect(feature.geometry).to.be.an('object');
expect(feature).to.have.property('properties');
expect(feature.properties).to.be.an('object');
var geometry = feature.geometry;
expect(geometry.type).to.be('Point');
expect(geometry).to.have.property('coordinates');
expect(geometry.coordinates).to.be.an('array');
expect(geometry.coordinates).to.have.length(2);
expect(geometry.coordinates[0]).to.be.a('number');
expect(geometry.coordinates[1]).to.be.a('number');
var props = feature.properties;
expect(props).to.be.an('object');
expect(props).to.only.have.keys(
'value', 'geohash', 'aggConfigResult',
'rectangle', 'center'
);
expect(props.center).to.eql(geometry.coordinates);
if (props.value != null) expect(props.value).to.be.a('number');
expect(props.geohash).to.be.a('string');
if (tableOpts.asAggConfigResults) {
expect(props.aggConfigResult).to.be(row[metricColI]);
expect(props.value).to.be(row[metricColI].value);
expect(props.geohash).to.be(row[geoColI].value);
} else {
expect(props.aggConfigResult).to.be(null);
expect(props.value).to.be(row[metricColI]);
expect(props.geohash).to.be(row[geoColI]);
}
});
});
});
});
});
describe('geoJson tooltip formatter', function () {});
});
});

View file

@ -1,58 +1,65 @@
define(function (require) {
describe('buildHierarchicalData()', function () {
describe('transformAggregation()', function () {
var _ = require('lodash');
var transform;
var fixture;
beforeEach(module('kibana'));
beforeEach(inject(function (Private) {
transform = Private(require('components/agg_response/hierarchical/_transform_aggregation'));
}));
var fixture = {};
fixture.agg = { id: 'agg_2', name: 'test', schema: { group: 'buckets' }, getKey: function () {},
_next: { id: 'agg_3', name: 'example', schema: { group: 'buckets' }, getKey: function () {} } };
fixture.metric = { id: 'agg_1' };
fixture.aggData = {
buckets: [
{ key: 'foo', doc_count: 2, agg_3: { buckets: [ { key: 'win', doc_count: 1 }, { key: 'mac', doc_count: 1 }]}},
{ key: 'bar', doc_count: 4, agg_3: { buckets: [ { key: 'win', doc_count: 2 }, { key: 'mac', doc_count: 2 }]}}
]
};
beforeEach(function () {
it('should return an array of objects with the doc_count as the size if the metric does not exist', function () {
var agg = { id: 'agg_2', name: 'test', schema: { group: 'buckets' }, getKey: function () {}};
var aggData = {
function fakeAgg(id, name) {
return {
id: id,
name: name,
schema: { group: 'buckets' },
getKey: _.noop,
fieldFormatter: _.constant(String)
};
}
fixture = {};
fixture.agg = fakeAgg('agg_2', 'test');
fixture.agg._next = fakeAgg('agg_3', 'example');
fixture.metric = fakeAgg('agg_1', 'metric');
fixture.metric.getValue = function (b) { return _.has(b, this.id) ? b[this.id] : b.doc_count; };
fixture.aggData = {
buckets: [
{ key: 'foo', doc_count: 1 },
{ key: 'bar', doc_count: 2 }
{ key: 'foo', doc_count: 2, agg_3: { buckets: [ { key: 'win', doc_count: 1 }, { key: 'mac', doc_count: 1 }]}},
{ key: 'bar', doc_count: 4, agg_3: { buckets: [ { key: 'win', doc_count: 2 }, { key: 'mac', doc_count: 2 }]}}
]
};
var children = transform(agg, fixture.metric, aggData);
expect(children).to.be.an(Array);
expect(children).to.have.length(2);
expect(children[0]).to.have.property('size', 1);
expect(children[1]).to.have.property('size', 2);
});
it('should return an array of objects with the metric agg value as the size', function () {
var agg = { id: 'agg_2', name: 'test', schema: { group: 'buckets' }, getKey: function () {} };
it('relies on metricAgg#getValue() for the size of the children', function () {
var aggData = {
buckets: [
{ key: 'foo', doc_count: 1, agg_1: { value: 0 } },
{ key: 'bar', doc_count: 2, agg_1: { value: 4 } }
{ key: 'foo' },
{ key: 'bar' }
]
};
var children = transform(agg, fixture.metric, aggData);
var football = {};
fixture.metric.getValue = _.constant(football);
var children = transform(fixture.agg, fixture.metric, aggData);
expect(children).to.be.an(Array);
expect(children).to.have.length(2);
expect(children[0]).to.have.property('size', 0);
expect(children[1]).to.have.property('size', 4);
expect(children[0]).to.have.property('size', football);
expect(children[1]).to.have.property('size', football);
});
it('should create two levels of metrics', function () {
var children = transform(fixture.agg, fixture.metric, fixture.aggData);
fixture.metric.getValue = function (b) { return b.doc_count; };
expect(children).to.be.an(Array);
expect(children).to.have.length(2);
expect(children[0]).to.have.property('children');

View file

@ -1,15 +1,14 @@
define(function (require) {
describe('Point Series Agg Response', function () {
run(require('specs/components/agg_response/point_series/_main'));
run(require('specs/components/agg_response/point_series/_add_to_siri'));
run(require('specs/components/agg_response/point_series/_fake_x_aspect'));
run(require('specs/components/agg_response/point_series/_get_aspects'));
run(require('specs/components/agg_response/point_series/_get_point'));
run(require('specs/components/agg_response/point_series/_get_series'));
run(require('specs/components/agg_response/point_series/_init_x_axis'));
run(require('specs/components/agg_response/point_series/_init_y_axis'));
run(require('specs/components/agg_response/point_series/_ordered_date_axis'));
run(require('specs/components/agg_response/point_series/_tooltip_formatter'));
function run(module) { describe(module[0], module[1]); }
describe(require('specs/components/agg_response/point_series/_main'));
describe(require('specs/components/agg_response/point_series/_add_to_siri'));
describe(require('specs/components/agg_response/point_series/_fake_x_aspect'));
describe(require('specs/components/agg_response/point_series/_get_aspects'));
describe(require('specs/components/agg_response/point_series/_get_point'));
describe(require('specs/components/agg_response/point_series/_get_series'));
describe(require('specs/components/agg_response/point_series/_init_x_axis'));
describe(require('specs/components/agg_response/point_series/_init_y_axis'));
describe(require('specs/components/agg_response/point_series/_ordered_date_axis'));
describe(require('specs/components/agg_response/point_series/_tooltip_formatter'));
});
});
});

View file

@ -1,11 +1,10 @@
define(function (require) {
describe('Tabify Agg Response', function () {
run(require('specs/components/agg_response/tabify/_get_columns'));
run(require('specs/components/agg_response/tabify/_buckets'));
run(require('specs/components/agg_response/tabify/_table'));
run(require('specs/components/agg_response/tabify/_table_group'));
run(require('specs/components/agg_response/tabify/_response_writer'));
run(require('specs/components/agg_response/tabify/_integration'));
function run(module) { describe(module[0], module[1]); }
describe(require('specs/components/agg_response/tabify/_get_columns'));
describe(require('specs/components/agg_response/tabify/_buckets'));
describe(require('specs/components/agg_response/tabify/_table'));
describe(require('specs/components/agg_response/tabify/_table_group'));
describe(require('specs/components/agg_response/tabify/_response_writer'));
describe(require('specs/components/agg_response/tabify/_integration'));
});
});
});

View file

@ -1,7 +1,6 @@
define(function (require) {
describe('AggTable Component', function () {
run(require('specs/components/agg_table/_group'));
run(require('specs/components/agg_table/_table'));
function run(mod) { describe(mod[0], mod[1]); }
describe(require('specs/components/agg_table/_group'));
describe(require('specs/components/agg_table/_table'));
});
});
});

View file

@ -1,151 +0,0 @@
define(function (require) {
return ['Date Histogram Agg', function () {
var _ = require('lodash');
var moment = require('moment');
describe('params', function () {
var paramWriter;
var writeInterval;
var aggTypes;
var AggConfig;
var setTimeBounds;
var timeField;
beforeEach(module('kibana'));
beforeEach(inject(function (Private, $injector) {
var AggParamWriter = Private(require('test_utils/agg_param_writer'));
var indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
var timefilter = $injector.get('timefilter');
timeField = indexPattern.timeFieldName;
aggTypes = Private(require('components/agg_types/index'));
AggConfig = Private(require('components/vis/_agg_config'));
paramWriter = new AggParamWriter({ aggType: 'date_histogram' });
writeInterval = function (interval) {
return paramWriter.write({ interval: interval, field: timeField });
};
var now = moment();
setTimeBounds = function (n, units) {
timefilter.enabled = true;
timefilter.getBounds = _.constant({
min: now.clone().subtract(n, units),
max: now.clone()
});
};
}));
describe('interval', function () {
it('accepts a valid interval', function () {
var output = writeInterval('d');
expect(output.params).to.have.property('interval', '1d');
});
it('ignores invalid intervals', function () {
var output = writeInterval('foo');
expect(output.params).to.have.property('interval', '0ms');
});
it('automatically picks an interval', function () {
setTimeBounds(15, 'm');
var output = writeInterval('auto');
expect(output.params.interval).to.be('30s');
});
it('scales up the interval if it will make too many buckets', function () {
setTimeBounds(30, 'm');
var output = writeInterval('s');
expect(output.params.interval).to.be('10s');
expect(output.metricScaleText).to.be('second');
expect(output.metricScale).to.be(0.1);
});
it('does not scale down the interval', function () {
setTimeBounds(1, 'm');
var output = writeInterval('h');
expect(output.params.interval).to.be('1h');
expect(output.metricScaleText).to.be(undefined);
expect(output.metricScale).to.be(undefined);
});
describe('only scales when all metrics are sum or count', function () {
var tests = [
[ false, 'avg', 'count', 'sum' ],
[ true, 'count', 'sum' ],
[ false, 'count', 'cardinality' ]
];
tests.forEach(function (test) {
var should = test.shift();
var typeNames = test.slice();
it(typeNames.join(', ') + ' should ' + (should ? '' : 'not') + ' scale', function () {
setTimeBounds(1, 'y');
var vis = paramWriter.vis;
vis.aggs.splice(0);
var histoConfig = new AggConfig(vis, {
type: aggTypes.byName.date_histogram,
schema: 'segment',
params: { interval: 's', field: timeField }
});
vis.aggs.push(histoConfig);
typeNames.forEach(function (type) {
vis.aggs.push(new AggConfig(vis, {
type: aggTypes.byName[type],
schema: 'metric'
}));
});
var output = histoConfig.write();
expect(_.has(output, 'metricScale')).to.be(should);
});
});
});
});
describe('extended_bounds', function () {
it('should write a long value if a moment passed in', function () {
var then = moment(0);
var now = moment(500);
var output = paramWriter.write({
extended_bounds: {
min: then,
max: now
}
});
expect(typeof output.params.extended_bounds.min).to.be('number');
expect(typeof output.params.extended_bounds.max).to.be('number');
expect(output.params.extended_bounds.min).to.be(then.valueOf());
expect(output.params.extended_bounds.max).to.be(now.valueOf());
});
it('should write a long if a long is passed', function () {
var then = 0;
var now = 500;
var output = paramWriter.write({
extended_bounds: {
min: then,
max: now
}
});
expect(typeof output.params.extended_bounds.min).to.be('number');
expect(typeof output.params.extended_bounds.max).to.be('number');
expect(output.params.extended_bounds.min).to.be(then.valueOf());
expect(output.params.extended_bounds.max).to.be(now.valueOf());
});
});
});
}];
});

View file

@ -0,0 +1,6 @@
define(function (require) {
return ['Date Histogram Agg', function () {
describe(require('specs/components/agg_types/buckets/date_histogram/_editor'));
describe(require('specs/components/agg_types/buckets/date_histogram/_params'));
}];
});

View file

@ -0,0 +1,123 @@
define(function (require) {
return ['editor', function () {
var _ = require('lodash');
var $ = require('jquery');
var indexPattern;
var vis;
var agg;
var render;
var $scope;
beforeEach(module('kibana'));
beforeEach(inject(function (Private, $injector, $compile) {
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
var Vis = Private(require('components/vis/vis'));
/**
* Render the AggParams editor for the date histogram aggregation
*
* @param {object} params - the agg params to give to the date_histogram
* by default
* @return {object} - object pointing to the different inputs, keys
* are the aggParam name and the value is an object
* with $el, $scope, and a few helpers for getting
* data from them.
*/
render = function (params) {
vis = new Vis(indexPattern, {
type: 'histogram',
aggs:[
{ schema: 'metric', type: 'avg', params: { field: 'bytes' } },
{ schema: 'segment', type: 'date_histogram', params: params || {} }
]
});
var $el = $('<vis-editor-agg-params agg="agg" group-name="groupName"></vis-editor-agg-params>');
var $parentScope = $injector.get('$rootScope').$new();
agg = $parentScope.agg = vis.aggs.bySchemaName.segment[0];
$parentScope.groupName = 'buckets';
$compile($el)($parentScope);
$scope = $el.scope();
$scope.$digest();
var $inputs = $('vis-agg-param-editor', $el);
return _.transform($inputs.toArray(), function (inputs, e) {
var $el = $(e);
var $scope = $el.scope();
inputs[$scope.aggParam.name] = {
$el: $el,
$scope: $scope,
$input: function () {
return $el.find('[ng-model]').first();
},
modelValue: function () {
return this.$input().controller('ngModel').$modelValue;
}
};
}, {});
};
}));
describe('random field/interval', function () {
var params;
var field;
var interval;
beforeEach(inject(function (Private) {
field = _.sample(indexPattern.fields);
interval = _.sample(Private(require('components/agg_types/buckets/_interval_options')));
params = render({ field: field, interval: interval });
}));
it('renders the field editor', function () {
expect(agg.params.field).to.be(field);
expect(params).to.have.property('field');
expect(params.field).to.have.property('$el');
expect(params.field.modelValue()).to.be(field);
});
it('renders the interval editor', function () {
expect(agg.params.interval).to.be(interval);
expect(params).to.have.property('interval');
expect(params.interval).to.have.property('$el');
expect(params.interval.modelValue()).to.be(interval);
});
});
describe('interval "auto" and indexPattern timeField', function () {
var params;
beforeEach(function () {
params = render({ field: indexPattern.timeFieldName, interval: 'auto' });
});
it('clears the interval when the field is changed', function () {
expect(params.interval.modelValue().val).to.be('auto');
expect(params.field.modelValue().name).to.be(indexPattern.timeFieldName);
var field = _.find(indexPattern.fields, function (f) {
return f.type === 'date' && f.name !== indexPattern.timeFieldName;
});
params.field.$input()
.find('option')
.filter(function () {
return $(this).text().trim() === field.name;
})
.prop('selected', true);
params.field.$input().change();
expect(params.interval.modelValue()).to.be(undefined);
});
});
}];
});

View file

@ -0,0 +1,149 @@
define(function (require) {
return ['params', function () {
var _ = require('lodash');
var moment = require('moment');
var paramWriter;
var writeInterval;
var aggTypes;
var AggConfig;
var setTimeBounds;
var timeField;
beforeEach(module('kibana'));
beforeEach(inject(function (Private, $injector) {
var AggParamWriter = Private(require('test_utils/agg_param_writer'));
var indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
var timefilter = $injector.get('timefilter');
timeField = indexPattern.timeFieldName;
aggTypes = Private(require('components/agg_types/index'));
AggConfig = Private(require('components/vis/_agg_config'));
paramWriter = new AggParamWriter({ aggType: 'date_histogram' });
writeInterval = function (interval) {
return paramWriter.write({ interval: interval, field: timeField });
};
var now = moment();
setTimeBounds = function (n, units) {
timefilter.enabled = true;
timefilter.getBounds = _.constant({
min: now.clone().subtract(n, units),
max: now.clone()
});
};
}));
describe('interval', function () {
it('accepts a valid interval', function () {
var output = writeInterval('d');
expect(output.params).to.have.property('interval', '1d');
});
it('ignores invalid intervals', function () {
var output = writeInterval('foo');
expect(output.params).to.have.property('interval', '0ms');
});
it('automatically picks an interval', function () {
setTimeBounds(15, 'm');
var output = writeInterval('auto');
expect(output.params.interval).to.be('30s');
});
it('scales up the interval if it will make too many buckets', function () {
setTimeBounds(30, 'm');
var output = writeInterval('s');
expect(output.params.interval).to.be('10s');
expect(output.metricScaleText).to.be('second');
expect(output.metricScale).to.be(0.1);
});
it('does not scale down the interval', function () {
setTimeBounds(1, 'm');
var output = writeInterval('h');
expect(output.params.interval).to.be('1h');
expect(output.metricScaleText).to.be(undefined);
expect(output.metricScale).to.be(undefined);
});
describe('only scales when all metrics are sum or count', function () {
var tests = [
[ false, 'avg', 'count', 'sum' ],
[ true, 'count', 'sum' ],
[ false, 'count', 'cardinality' ]
];
tests.forEach(function (test) {
var should = test.shift();
var typeNames = test.slice();
it(typeNames.join(', ') + ' should ' + (should ? '' : 'not') + ' scale', function () {
setTimeBounds(1, 'y');
var vis = paramWriter.vis;
vis.aggs.splice(0);
var histoConfig = new AggConfig(vis, {
type: aggTypes.byName.date_histogram,
schema: 'segment',
params: { interval: 's', field: timeField }
});
vis.aggs.push(histoConfig);
typeNames.forEach(function (type) {
vis.aggs.push(new AggConfig(vis, {
type: aggTypes.byName[type],
schema: 'metric'
}));
});
var output = histoConfig.write();
expect(_.has(output, 'metricScale')).to.be(should);
});
});
});
});
describe('extended_bounds', function () {
it('should write a long value if a moment passed in', function () {
var then = moment(0);
var now = moment(500);
var output = paramWriter.write({
extended_bounds: {
min: then,
max: now
}
});
expect(typeof output.params.extended_bounds.min).to.be('number');
expect(typeof output.params.extended_bounds.max).to.be('number');
expect(output.params.extended_bounds.min).to.be(then.valueOf());
expect(output.params.extended_bounds.max).to.be(now.valueOf());
});
it('should write a long if a long is passed', function () {
var then = 0;
var now = 500;
var output = paramWriter.write({
extended_bounds: {
min: then,
max: now
}
});
expect(typeof output.params.extended_bounds.min).to.be('number');
expect(typeof output.params.extended_bounds.max).to.be('number');
expect(output.params.extended_bounds.min).to.be(then.valueOf());
expect(output.params.extended_bounds.max).to.be(now.valueOf());
});
});
}];
});

View file

@ -1,14 +1,9 @@
define(function (require) {
describe('AggTypesComponent', function () {
var childSuites = [
require('specs/components/agg_types/_agg_type'),
require('specs/components/agg_types/_agg_params'),
require('specs/components/agg_types/_bucket_count_between'),
require('specs/components/agg_types/buckets/_histogram'),
require('specs/components/agg_types/buckets/_date_histogram')
].forEach(function (s) {
describe(s[0], s[1]);
});
describe(require('specs/components/agg_types/_agg_type'));
describe(require('specs/components/agg_types/_agg_params'));
describe(require('specs/components/agg_types/_bucket_count_between'));
describe(require('specs/components/agg_types/buckets/_histogram'));
describe('bucket aggs', function () {
var bucketAggs;
@ -44,4 +39,4 @@ define(function (require) {
});
});
});
});
});

View file

@ -1,13 +1,9 @@
define(function (require) {
describe('ParamTypes', function () {
var childSuites = [
require('specs/components/agg_types/param_types/_field'),
require('specs/components/agg_types/param_types/_optioned'),
require('specs/components/agg_types/param_types/_regex'),
require('specs/components/agg_types/param_types/_string'),
require('specs/components/agg_types/param_types/_raw_json')
].forEach(function (s) {
describe(s[0], s[1]);
});
describe(require('specs/components/agg_types/param_types/_field'));
describe(require('specs/components/agg_types/param_types/_optioned'));
describe(require('specs/components/agg_types/param_types/_regex'));
describe(require('specs/components/agg_types/param_types/_string'));
describe(require('specs/components/agg_types/param_types/_raw_json'));
});
});
});

View file

@ -20,20 +20,24 @@ define(function (require) {
filters = [
{
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
meta: { negate: false, disabled: false }
meta: { index: 'logstash-*', negate: false, disabled: false }
},
{
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
meta: { negate: false, disabled: false }
meta: { index: 'logstash-*', negate: false, disabled: false }
},
{
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
meta: { negate: false, disabled: false }
meta: { index: 'logstash-*', negate: false, disabled: false }
}
];
});
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
return function () {
@ -56,19 +60,31 @@ define(function (require) {
describe('adding filters', function () {
it('should add filters to appState', function () {
$rootScope.$digest();
queryFilter.addFilters(filters);
$rootScope.$digest();
expect(appState.filters.length).to.be(3);
expect(globalState.filters.length).to.be(0);
});
it('should add filters to globalState', function () {
$rootScope.$digest();
queryFilter.addFilters(filters, true);
$rootScope.$digest();
expect(appState.filters.length).to.be(0);
expect(globalState.filters.length).to.be(3);
});
it('should accept a single filter', function () {
$rootScope.$digest();
queryFilter.addFilters(filters[0]);
$rootScope.$digest();
expect(appState.filters.length).to.be(1);
expect(globalState.filters.length).to.be(0);
});
@ -76,15 +92,20 @@ define(function (require) {
it('should fire the update and fetch events', function () {
var emitSpy = sinon.spy(queryFilter, 'emit');
// set up the watchers
// set up the watchers, add new filters, and crank the digest loop
$rootScope.$digest();
queryFilter.addFilters(filters);
// trigger the digest loop to fire the watchers
$rootScope.$digest();
// updates should trigger state saves
expect(appState.save.callCount).to.be(1);
expect(globalState.save.callCount).to.be(1);
// this time, events should be emitted
expect(emitSpy.callCount).to.be(2);
expect(emitSpy.firstCall.args[0]).to.be('update');
expect(emitSpy.secondCall.args[0]).to.be('fetch');
});
});
@ -92,6 +113,7 @@ define(function (require) {
it('should de-dupe appState filters being added', function () {
var newFilter = _.cloneDeep(filters[1]);
appState.filters = filters;
$rootScope.$digest();
expect(appState.filters.length).to.be(3);
queryFilter.addFilters(newFilter);
@ -102,6 +124,7 @@ define(function (require) {
it('should de-dupe globalState filters being added', function () {
var newFilter = _.cloneDeep(filters[1]);
globalState.filters = filters;
$rootScope.$digest();
expect(globalState.filters.length).to.be(3);
queryFilter.addFilters(newFilter, true);
@ -112,10 +135,12 @@ define(function (require) {
it('should mutate global filters on appState filter changes', function () {
var idx = 1;
globalState.filters = filters;
$rootScope.$digest();
var appFilter = _.cloneDeep(filters[idx]);
appFilter.meta.negate = true;
// use addFilters here, so custom adding logic can be applied
queryFilter.addFilters(appFilter);
$rootScope.$digest();
var res = queryFilter.getFilters();
expect(res).to.have.length(3);

View file

@ -34,6 +34,10 @@ define(function (require) {
});
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
return function () {
@ -109,6 +113,16 @@ define(function (require) {
expect(filter.meta.negate).to.be(false);
});
});
it('should work without global state filters', function () {
// remove global filters
delete globalState.filters;
queryFilter.invertAll();
_.each(appState.filters, function (filter) {
expect(filter.meta.negate).to.be(true);
});
});
});
}];
});

View file

@ -54,6 +54,10 @@ define(function (require) {
});
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
return function () {
@ -106,13 +110,11 @@ define(function (require) {
it('should only fire the update event', function () {
var filter = appState.filters[1];
var emitSpy = sinon.spy(queryFilter, 'emit');
// set up the watchers
var filter = appState.filters[1];
$rootScope.$digest();
queryFilter.pinFilter(filter);
// trigger the digest loop to fire the watchers
$rootScope.$digest();
expect(emitSpy.callCount).to.be(1);

View file

@ -34,6 +34,10 @@ define(function (require) {
});
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
return function () {
@ -72,11 +76,9 @@ define(function (require) {
it('should fire the update and fetch events', function () {
var emitSpy = sinon.spy(queryFilter, 'emit');
appState.filters = filters;
// set up the watchers
$rootScope.$digest();
queryFilter.removeFilter(filters[0]);
// trigger the digest loop to fire the watchers
$rootScope.$digest();
expect(emitSpy.callCount).to.be(2);
@ -84,16 +86,48 @@ define(function (require) {
expect(emitSpy.secondCall.args[0]).to.be('fetch');
});
it('should only remove matching instances', function () {
it('should remove matching filters', function () {
globalState.filters.push(filters[0]);
globalState.filters.push(filters[1]);
appState.filters.push(filters[2]);
$rootScope.$digest();
queryFilter.removeFilter(filters[0]);
$rootScope.$digest();
expect(globalState.filters).to.have.length(1);
expect(appState.filters).to.have.length(1);
});
it('should remove matching filters by comparison', function () {
globalState.filters.push(filters[0]);
globalState.filters.push(filters[1]);
appState.filters.push(filters[2]);
$rootScope.$digest();
queryFilter.removeFilter(_.cloneDeep(filters[0]));
expect(globalState.filters).to.have.length(2);
$rootScope.$digest();
expect(globalState.filters).to.have.length(1);
expect(appState.filters).to.have.length(1);
queryFilter.removeFilter(_.cloneDeep(filters[2]));
$rootScope.$digest();
expect(globalState.filters).to.have.length(1);
expect(appState.filters).to.have.length(0);
});
it('should do nothing with a non-matching filter', function () {
globalState.filters.push(filters[0]);
globalState.filters.push(filters[1]);
appState.filters.push(filters[2]);
$rootScope.$digest();
var missedFilter = _.cloneDeep(filters[0]);
missedFilter.meta = {
negate: !filters[0].meta.negate
};
queryFilter.removeFilter(missedFilter);
$rootScope.$digest();
expect(globalState.filters).to.have.length(2);
expect(appState.filters).to.have.length(1);
});

View file

@ -34,6 +34,10 @@ define(function (require) {
});
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
return function () {
@ -78,11 +82,9 @@ define(function (require) {
it('should fire the update and fetch events', function () {
var emitSpy = sinon.spy(queryFilter, 'emit');
appState.filters = filters;
// set up the watchers
$rootScope.$digest();
queryFilter.toggleFilter(filters[1]);
// trigger the digest loop to fire the watchers
$rootScope.$digest();
expect(emitSpy.callCount).to.be(2);
@ -111,7 +113,16 @@ define(function (require) {
expect(globalState.filters[1].meta.disabled).to.be(true);
queryFilter.toggleFilter(filters[1], true);
expect(globalState.filters[1].meta.disabled).to.be(true);
});
it('should work without appState', function () {
appState = undefined;
globalState.filters = filters;
expect(globalState.filters[1].meta.disabled).to.be(false);
expect(queryFilter.getFilters()).to.have.length(3);
queryFilter.toggleFilter(filters[1]);
expect(globalState.filters[1].meta.disabled).to.be(true);
});
});
@ -153,6 +164,21 @@ define(function (require) {
expect(filter.meta.disabled).to.be(false);
});
});
it('should work without appState', function () {
appState = undefined;
globalState.filters = filters;
_.each(globalState.filters, function (filter) {
expect(filter.meta.disabled).to.be(false);
});
queryFilter.toggleAll();
_.each(globalState.filters, function (filter) {
expect(filter.meta.disabled).to.be(true);
});
});
});
}];
});

View file

@ -3,27 +3,19 @@ define(function (require) {
describe('extractTimeFilter()', function () {
var sinon = require('test_utils/auto_release_sinon');
var extractTimeFilter,
$rootScope,
indexPattern,
getIndexPatternStub;
$rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
extractTimeFilter = Private(require('components/filter_bar/lib/extractTimeFilter'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the matching filter for the defualt time field', function (done) {

View file

@ -4,27 +4,19 @@ define(function (require) {
var sinon = require('test_utils/auto_release_sinon');
var filterOutTimeBasedFilter,
$rootScope,
indexPattern,
getIndexPatternStub;
$rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
filterOutTimeBasedFilter = Private(require('components/filter_bar/lib/filterOutTimeBasedFilter'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the matching filter for the defualt time field', function (done) {

View file

@ -1,7 +1,6 @@
define(function (require) {
var angular = require('angular');
var _ = require('lodash');
var sinon = require('sinon/sinon');
var $ = require('jquery');
require('components/filter_bar/filter_bar');
@ -9,7 +8,7 @@ define(function (require) {
describe('Filter Bar Directive', function () {
var $rootScope, $compile, $timeout, Promise;
var appState, queryFilter, mapFilter, getIndexPatternStub, indexPattern, $el;
var appState, queryFilter, mapFilter, $el;
// require('test_utils/no_digest_promises').activateForSuite();
beforeEach(function () {
@ -28,13 +27,8 @@ define(function (require) {
// load the application
module('kibana');
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
inject(function (Private, $injector, _$rootScope_, _$compile_, _$timeout_) {
@ -44,9 +38,6 @@ define(function (require) {
Promise = $injector.get('Promise');
mapFilter = Private(require('components/filter_bar/lib/mapFilter'));
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
var queryFilter = Private(require('components/filter_bar/query_filter'));
queryFilter.getFilters = function () {
return appState.filters;

View file

@ -2,25 +2,19 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapAndFlattenFilters()', function () {
var sinon = require('test_utils/auto_release_sinon');
var mapAndFlattenFilters, $rootScope, indexPattern, getIndexPatternStub;
var mapAndFlattenFilters, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
mapAndFlattenFilters = Private(require('components/filter_bar/lib/mapAndFlattenFilters'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
var filters = [

View file

@ -1,25 +1,19 @@
define(function (require) {
describe('Filter Bar Directive', function () {
var sinon = require('test_utils/auto_release_sinon');
var mapFilter, $rootScope, indexPattern, getIndexPatternStub;
var mapFilter, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Promise, _$rootScope_, Private) {
beforeEach(inject(function (_$rootScope_, Private) {
mapFilter = Private(require('components/filter_bar/lib/mapFilter'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
describe('mapFilter()', function () {

View file

@ -3,24 +3,18 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapFlattenAndWrapFilters()', function () {
var sinon = require('test_utils/auto_release_sinon');
var mapFlattenAndWrapFilters, $rootScope, indexPattern, getIndexPatternStub;
var mapFlattenAndWrapFilters, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
mapFlattenAndWrapFilters = Private(require('components/filter_bar/lib/mapFlattenAndWrapFilters'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
var filters = [

View file

@ -2,24 +2,18 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapGeoBoundingBox()', function () {
var sinon = require('test_utils/auto_release_sinon');
var mapGeoBoundingBox, $rootScope, indexPattern, getIndexPatternStub;
var mapGeoBoundingBox, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
mapGeoBoundingBox = Private(require('components/filter_bar/lib/mapGeoBoundingBox'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the key and value for matching filters with bounds', function (done) {

View file

@ -2,24 +2,18 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapRange()', function () {
var sinon = require('test_utils/auto_release_sinon');
var mapRange, $rootScope, indexPattern, getIndexPatternStub;
var mapRange, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
mapRange = Private(require('components/filter_bar/lib/mapRange'));
$rootScope = _$rootScope_;
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the key and value for matching filters with gt/lt', function (done) {

View file

@ -2,24 +2,18 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapScript()', function () {
var sinon = require('test_utils/auto_release_sinon');
var indexPattern, mapScript, $rootScope, getIndexPatternStub;
var mapScript, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapScript = Private(require('components/filter_bar/lib/mapScript'));
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the key and value for matching filters', function (done) {

View file

@ -2,24 +2,18 @@ define(function (require) {
describe('Filter Bar Directive', function () {
describe('mapTerms()', function () {
var sinon = require('test_utils/auto_release_sinon');
var indexPattern, mapTerms, $rootScope, getIndexPatternStub;
var mapTerms, $rootScope;
beforeEach(module('kibana'));
beforeEach(function () {
getIndexPatternStub = sinon.stub();
module('kibana/courier', function ($provide) {
$provide.service('courier', function () {
var courier = { indexPatterns: { get: getIndexPatternStub } };
return courier;
});
$provide.service('courier', require('fixtures/mock_courier'));
});
});
beforeEach(inject(function (Private, _$rootScope_, Promise) {
beforeEach(inject(function (Private, _$rootScope_) {
$rootScope = _$rootScope_;
mapTerms = Private(require('components/filter_bar/lib/mapTerms'));
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
getIndexPatternStub.returns(Promise.resolve(indexPattern));
}));
it('should return the key and value for matching filters', function (done) {

View file

@ -44,16 +44,12 @@ define(function (require) {
});
describe('Actions', function () {
var childSuites = [
require('specs/components/filter_bar/_getFilters'),
require('specs/components/filter_bar/_addFilters'),
require('specs/components/filter_bar/_removeFilters'),
require('specs/components/filter_bar/_toggleFilters'),
require('specs/components/filter_bar/_invertFilters'),
require('specs/components/filter_bar/_pinFilters'),
].forEach(function (s) {
describe(s[0], s[1]);
});
describe(require('specs/components/filter_bar/_getFilters'));
describe(require('specs/components/filter_bar/_addFilters'));
describe(require('specs/components/filter_bar/_removeFilters'));
describe(require('specs/components/filter_bar/_toggleFilters'));
describe(require('specs/components/filter_bar/_invertFilters'));
describe(require('specs/components/filter_bar/_pinFilters'));
});
});
});

View file

@ -1,13 +1,30 @@
define(function (require) {
var _ = require('lodash');
var sinon = require('test_utils/auto_release_sinon');
var MockState = require('fixtures/mock_state');
var $rootScope;
var queryFilter;
var filterManager;
var appState;
function checkAddFilters(length, comps, idx) {
idx = idx || 0;
var filters = queryFilter.addFilters.getCall(idx).args[0];
expect(filters.length).to.be(length);
if (!_.isArray(comps)) return;
comps.forEach(function (comp, i) {
expect(filters[i]).to.eql(comp);
});
}
describe('Filter Manager', function () {
beforeEach(module('kibana'));
beforeEach(function () {
module('kibana/courier', function ($provide) {
$provide.service('courier', require('fixtures/mock_courier'));
});
module('kibana/global_state', function ($provide) {
$provide.service('getAppState', function () {
@ -19,16 +36,24 @@ define(function (require) {
});
beforeEach(function () {
inject(function (Private) {
inject(function (_$rootScope_, Private) {
$rootScope = _$rootScope_;
filterManager = Private(require('components/filter_manager/filter_manager'));
appState = new MockState();
appState.filters = [];
// mock queryFilter's invertFilter since it's used in the manager
var queryFilter = Private(require('components/filter_bar/query_filter'));
queryFilter.invertFilter = function (filter) {
// mock required queryFilter methods, used in the manager
queryFilter = Private(require('components/filter_bar/query_filter'));
sinon.stub(queryFilter, 'getAppFilters', function () {
return appState.filters;
});
sinon.stub(queryFilter, 'addFilters', function (filters) {
if (!_.isArray(filters)) filters = [filters];
appState.filters = appState.filters.concat(filters);
});
sinon.stub(queryFilter, 'invertFilter', function (filter) {
filter.meta.negate = !filter.meta.negate;
};
});
});
});
@ -37,33 +62,60 @@ define(function (require) {
});
it('should add a filter', function () {
expect(appState.filters.length).to.be(0);
filterManager.add('myField', 1, '+', 'myIndex');
expect(appState.filters.length).to.be(1);
expect(queryFilter.addFilters.callCount).to.be(1);
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
query: { match: { myField: { query: 1, type: 'phrase'} } }
}]);
});
it('should add multiple filters if passed an array of values', function () {
filterManager.add('myField', [1, 2, 3], '+', 'myIndex');
expect(appState.filters.length).to.be(3);
expect(queryFilter.addFilters.callCount).to.be(1);
checkAddFilters(3, [{
meta: { index: 'myIndex', negate: false },
query: { match: { myField: { query: 1, type: 'phrase'} } }
}, {
meta: { index: 'myIndex', negate: false },
query: { match: { myField: { query: 2, type: 'phrase'} } }
}, {
meta: { index: 'myIndex', negate: false },
query: { match: { myField: { query: 3, type: 'phrase'} } }
}]);
});
it('should add an exists filter if _exists_ is used as the field', function () {
filterManager.add('_exists_', 'myField', '+', 'myIndex');
expect(appState.filters[0].exists).to.eql({field: 'myField'});
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
exists: { field: 'myField' }
}]);
});
it('Should negate existing filter instead of added a conflicting filter', function () {
it('should negate existing filter instead of added a conflicting filter', function () {
filterManager.add('myField', 1, '+', 'myIndex');
expect(appState.filters.length).to.be(1);
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
query: { match: { myField: { query: 1, type: 'phrase'} } }
}], 0);
expect(appState.filters).to.have.length(1);
// NOTE: negating exists filters also forces disabled to false
filterManager.add('myField', 1, '-', 'myIndex');
expect(appState.filters.length).to.be(1);
expect(appState.filters[0].meta.negate).to.be(true);
checkAddFilters(0, null, 1);
expect(appState.filters).to.have.length(1);
filterManager.add('_exists_', 'myField', '+', 'myIndex');
expect(appState.filters.length).to.be(2);
checkAddFilters(1, [{
meta: { index: 'myIndex', negate: false },
exists: { field: 'myField' }
}], 2);
expect(appState.filters).to.have.length(2);
filterManager.add('_exists_', 'myField', '-', 'myIndex');
expect(appState.filters.length).to.be(2);
expect(appState.filters[1].meta.negate).to.be(true);
checkAddFilters(0, null, 3);
expect(appState.filters).to.have.length(2);
});
it('should enable matching filters being changed', function () {

View file

@ -1,12 +1,10 @@
define(function (require) {
describe('Index Patterns', function () {
run(require('specs/components/index_pattern/_index_pattern'));
run(require('specs/components/index_pattern/_cast_mapping_type'));
run(require('specs/components/index_pattern/_map_field'));
run(require('specs/components/index_pattern/_pattern_to_wildcard'));
run(require('specs/components/index_pattern/_get_computed_fields'));
run(require('specs/components/index_pattern/_FieldFormat'));
function run(mod) { describe(mod[0], mod[1]); }
describe(require('specs/components/index_pattern/_index_pattern'));
describe(require('specs/components/index_pattern/_cast_mapping_type'));
describe(require('specs/components/index_pattern/_map_field'));
describe(require('specs/components/index_pattern/_pattern_to_wildcard'));
describe(require('specs/components/index_pattern/_get_computed_fields'));
describe(require('specs/components/index_pattern/_FieldFormat'));
});
});

View file

@ -1,12 +1,8 @@
define(function (require) {
describe('Stringify Component', function () {
run(require('specs/components/stringify/_conformance'));
run(require('specs/components/stringify/_ip'));
run(require('specs/components/stringify/_source'));
run(require('specs/components/stringify/_url'));
function run(suite) {
describe(suite[0], suite[1]);
}
describe(require('specs/components/stringify/_conformance'));
describe(require('specs/components/stringify/_ip'));
describe(require('specs/components/stringify/_source'));
describe(require('specs/components/stringify/_url'));
});
});

View file

@ -1,10 +1,6 @@
define(function (require) {
describe('Vis Component', function () {
var childSuites = [
require('specs/components/vis/_agg_config'),
require('specs/components/vis/_agg_configs')
].forEach(function (s) {
describe(s[0], s[1]);
});
describe(require('specs/components/vis/_agg_config'));
describe(require('specs/components/vis/_agg_configs'));
});
});
});

View file

@ -1,7 +1,6 @@
define(function (require) {
describe('Table Vis', function () {
function run(m) { describe(m[0], m[1]); }
run(require('specs/plugins/table_vis/_table_vis_controller'));
run(require('specs/plugins/table_vis/_table_vis'));
describe(require('specs/plugins/table_vis/_table_vis_controller'));
describe(require('specs/plugins/table_vis/_table_vis'));
});
});
});

View file

@ -1,10 +1,6 @@
define(function (require) {
describe('Vis Type', function () {
var childSuites = [
require('specs/plugins/vis_types/_renderbot'),
require('specs/plugins/vis_types/vislib/_index')
].forEach(function (s) {
describe(s[0], s[1]);
});
describe(require('specs/plugins/vis_types/_renderbot'));
describe(require('specs/plugins/vis_types/vislib/_index'));
});
});
});

View file

@ -1,9 +1,6 @@
define(function (require) {
return ['Vislib', exportWrapper];
function exportWrapper() {
run(require('specs/plugins/vis_types/vislib/_renderbot'));
run(require('specs/plugins/vis_types/vislib/_build_chart_data'));
function run(m) { describe(m[0], m[1]); }
}
});
return ['Vislib', function () {
describe(require('specs/plugins/vis_types/vislib/_renderbot'));
describe(require('specs/plugins/vis_types/vislib/_build_chart_data'));
}];
});

View file

@ -1,8 +1,7 @@
define(function (require) {
describe('lodash mixins', function () {
run(require('specs/utils/mixins/_move'));
run(require('specs/utils/mixins/_organize_by'));
run(require('specs/utils/mixins/_push_all'));
function run(m) { describe(m[0], m[1]); }
describe(require('specs/utils/mixins/_move'));
describe(require('specs/utils/mixins/_organize_by'));
describe(require('specs/utils/mixins/_push_all'));
});
});

View file

@ -1,7 +1,10 @@
define(function () {
define(function (require) {
var _ = require('lodash');
return {
'columns': [
{
'title': 'Top 2 geo.dest: CN',
'valueFormatter': _.identity,
'geoJson': {
'type': 'FeatureCollection',
'features': [
@ -15,8 +18,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 42,
'value': 42,
'geohash': 's',
'center': [
22.5,
@ -95,8 +97,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 31,
'value': 31,
'geohash': 'd',
'center': [
-67.5,
@ -175,8 +176,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 30,
'value': 30,
'geohash': 'w',
'center': [
112.5,
@ -255,8 +255,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 25,
'value': 25,
'geohash': '9',
'center': [
-112.5,
@ -335,8 +334,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 22,
'value': 22,
'geohash': 't',
'center': [
67.5,
@ -415,8 +413,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 22,
'value': 22,
'geohash': 'k',
'center': [
22.5,
@ -495,8 +492,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 21,
'value': 21,
'geohash': '6',
'center': [
-67.5,
@ -575,8 +571,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 19,
'value': 19,
'geohash': 'u',
'center': [
22.5,
@ -655,8 +650,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 18,
'value': 18,
'geohash': 'v',
'center': [
67.5,
@ -735,8 +729,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 11,
'value': 11,
'geohash': 'c',
'center': [
-112.5,
@ -815,8 +808,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 10,
'value': 10,
'geohash': 'r',
'center': [
157.5,
@ -895,8 +887,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 9,
'value': 9,
'geohash': 'y',
'center': [
112.5,
@ -975,8 +966,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 9,
'value': 9,
'geohash': 'e',
'center': [
-22.5,
@ -1055,8 +1045,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 8,
'value': 8,
'geohash': 'f',
'center': [
-67.5,
@ -1135,8 +1124,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 8,
'value': 8,
'geohash': '7',
'center': [
-22.5,
@ -1215,8 +1203,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'q',
'center': [
112.5,
@ -1295,8 +1282,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'g',
'center': [
-22.5,
@ -1375,8 +1361,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 4,
'value': 4,
'geohash': 'x',
'center': [
157.5,
@ -1455,8 +1440,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 3,
'value': 3,
'geohash': 'b',
'center': [
-157.5,
@ -1535,8 +1519,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': 'z',
'center': [
157.5,
@ -1615,8 +1598,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'm',
'center': [
67.5,
@ -1695,8 +1677,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '5',
'center': [
-22.5,
@ -1775,8 +1756,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '4',
'center': [
-67.5,
@ -1855,8 +1835,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '3',
'center': [
-112.5,
@ -1927,16 +1906,14 @@ define(function () {
}
],
'properties': {
'label': 'Top 2 geo.dest: CN',
'length': 24,
'min': 1,
'max': 42,
'precision': 1
'max': 42
}
},
'label': 'Top 2 geo.dest: CN'
}
},
{
'label': 'Top 2 geo.dest: IN',
'valueFormatter': _.identity,
'geoJson': {
'type': 'FeatureCollection',
'features': [
@ -1950,8 +1927,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 32,
'value': 32,
'geohash': 's',
'center': [
22.5,
@ -2030,8 +2006,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 31,
'value': 31,
'geohash': '6',
'center': [
-67.5,
@ -2110,8 +2085,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 28,
'value': 28,
'geohash': 'd',
'center': [
-67.5,
@ -2190,8 +2164,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 27,
'value': 27,
'geohash': 'w',
'center': [
112.5,
@ -2270,8 +2243,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 24,
'value': 24,
'geohash': 't',
'center': [
67.5,
@ -2350,8 +2322,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 23,
'value': 23,
'geohash': 'k',
'center': [
22.5,
@ -2430,8 +2401,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 17,
'value': 17,
'geohash': 'u',
'center': [
22.5,
@ -2510,8 +2480,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 16,
'value': 16,
'geohash': '9',
'center': [
-112.5,
@ -2590,8 +2559,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 14,
'value': 14,
'geohash': 'v',
'center': [
67.5,
@ -2670,8 +2638,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 13,
'value': 13,
'geohash': 'e',
'center': [
-22.5,
@ -2750,8 +2717,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 9,
'value': 9,
'geohash': 'r',
'center': [
157.5,
@ -2830,8 +2796,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'y',
'center': [
112.5,
@ -2910,8 +2875,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'g',
'center': [
-22.5,
@ -2990,8 +2954,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'f',
'center': [
-67.5,
@ -3070,8 +3033,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 5,
'value': 5,
'geohash': 'c',
'center': [
-112.5,
@ -3150,8 +3112,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 4,
'value': 4,
'geohash': 'b',
'center': [
-157.5,
@ -3230,8 +3191,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 3,
'value': 3,
'geohash': 'q',
'center': [
112.5,
@ -3310,8 +3270,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': '4',
'center': [
-67.5,
@ -3390,8 +3349,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'z',
'center': [
157.5,
@ -3470,8 +3428,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'x',
'center': [
157.5,
@ -3550,8 +3507,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'p',
'center': [
157.5,
@ -3630,8 +3586,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'm',
'center': [
67.5,
@ -3710,8 +3665,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '7',
'center': [
-22.5,
@ -3782,16 +3736,11 @@ define(function () {
}
],
'properties': {
'label': 'Top 2 geo.dest: IN',
'length': 23,
'min': 1,
'max': 32,
'precision': 1
'max': 32
}
},
'label': 'Top 2 geo.dest: IN'
}
}
],
'hits': 1638
};
]
};
});

View file

@ -1,5 +1,8 @@
define(function () {
define(function (require) {
var _ = require('lodash');
return {
'valueFormatter': _.identity,
'geoJson': {
'type': 'FeatureCollection',
'features': [
@ -13,8 +16,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 608,
'value': 608,
'geohash': 's',
'center': [
22.5,
@ -75,8 +77,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 522,
'value': 522,
'geohash': 'w',
'center': [
112.5,
@ -137,8 +138,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 517,
'value': 517,
'geohash': '6',
'center': [
-67.5,
@ -199,8 +199,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 446,
'value': 446,
'geohash': 'd',
'center': [
-67.5,
@ -261,8 +260,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 426,
'value': 426,
'geohash': 'u',
'center': [
22.5,
@ -323,8 +321,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 413,
'value': 413,
'geohash': 't',
'center': [
67.5,
@ -385,8 +382,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 362,
'value': 362,
'geohash': 'k',
'center': [
22.5,
@ -447,8 +443,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 352,
'value': 352,
'geohash': '9',
'center': [
-112.5,
@ -509,8 +504,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 216,
'value': 216,
'geohash': 'e',
'center': [
-22.5,
@ -571,8 +565,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 183,
'value': 183,
'geohash': 'v',
'center': [
67.5,
@ -633,8 +626,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 158,
'value': 158,
'geohash': 'r',
'center': [
157.5,
@ -695,8 +687,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 139,
'value': 139,
'geohash': 'y',
'center': [
112.5,
@ -757,8 +748,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 110,
'value': 110,
'geohash': 'c',
'center': [
-112.5,
@ -819,8 +809,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 101,
'value': 101,
'geohash': 'q',
'center': [
112.5,
@ -881,8 +870,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 101,
'value': 101,
'geohash': '7',
'center': [
-22.5,
@ -943,8 +931,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 92,
'value': 92,
'geohash': 'f',
'center': [
-67.5,
@ -1005,8 +992,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 75,
'value': 75,
'geohash': 'b',
'center': [
-157.5,
@ -1067,8 +1053,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 64,
'value': 64,
'geohash': 'g',
'center': [
-22.5,
@ -1129,8 +1114,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 36,
'value': 36,
'geohash': 'z',
'center': [
157.5,
@ -1191,8 +1175,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 34,
'value': 34,
'geohash': 'x',
'center': [
157.5,
@ -1253,8 +1236,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 30,
'value': 30,
'geohash': '4',
'center': [
-67.5,
@ -1315,8 +1297,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 16,
'value': 16,
'geohash': 'm',
'center': [
67.5,
@ -1377,8 +1358,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 10,
'value': 10,
'geohash': '5',
'center': [
-22.5,
@ -1439,8 +1419,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'p',
'center': [
157.5,
@ -1501,8 +1480,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': '2',
'center': [
-157.5,
@ -1563,8 +1541,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 4,
'value': 4,
'geohash': 'h',
'center': [
22.5,
@ -1625,8 +1602,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': 'n',
'center': [
112.5,
@ -1687,8 +1663,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': 'j',
'center': [
67.5,
@ -1749,8 +1724,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '3',
'center': [
-112.5,
@ -1811,8 +1785,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '1',
'center': [
-112.5,
@ -1865,15 +1838,9 @@ define(function () {
}
],
'properties': {
'label': null,
'length': 30,
'min': 1,
'max': 608,
'precision': 1,
'allmin': 1,
'allmax': 608
'max': 608
}
},
'hits': 5033
};
});

View file

@ -1,7 +1,11 @@
define(function () {
define(function (require) {
var _ = require('lodash');
return {
'rows': [
{
'title': 'Top 2 geo.dest: CN',
'valueFormatter': _.identity,
'geoJson': {
'type': 'FeatureCollection',
'features': [
@ -15,8 +19,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 39,
'value': 39,
'geohash': 's',
'center': [
22.5,
@ -95,8 +98,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 31,
'value': 31,
'geohash': 'w',
'center': [
112.5,
@ -175,8 +177,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 30,
'value': 30,
'geohash': 'd',
'center': [
-67.5,
@ -255,8 +256,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 25,
'value': 25,
'geohash': '9',
'center': [
-112.5,
@ -335,8 +335,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 23,
'value': 23,
'geohash': 't',
'center': [
67.5,
@ -415,8 +414,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 23,
'value': 23,
'geohash': 'k',
'center': [
22.5,
@ -495,8 +493,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 22,
'value': 22,
'geohash': '6',
'center': [
-67.5,
@ -575,8 +572,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 20,
'value': 20,
'geohash': 'u',
'center': [
22.5,
@ -655,8 +651,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 18,
'value': 18,
'geohash': 'v',
'center': [
67.5,
@ -735,8 +730,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 11,
'value': 11,
'geohash': 'r',
'center': [
157.5,
@ -815,8 +809,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 11,
'value': 11,
'geohash': 'e',
'center': [
-22.5,
@ -895,8 +888,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 10,
'value': 10,
'geohash': 'y',
'center': [
112.5,
@ -975,8 +967,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 10,
'value': 10,
'geohash': 'c',
'center': [
-112.5,
@ -1055,8 +1046,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 8,
'value': 8,
'geohash': 'f',
'center': [
-67.5,
@ -1135,8 +1125,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 8,
'value': 8,
'geohash': '7',
'center': [
-22.5,
@ -1215,8 +1204,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'q',
'center': [
112.5,
@ -1295,8 +1283,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'g',
'center': [
-22.5,
@ -1375,8 +1362,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 4,
'value': 4,
'geohash': 'x',
'center': [
157.5,
@ -1455,8 +1441,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 3,
'value': 3,
'geohash': 'b',
'center': [
-157.5,
@ -1535,8 +1520,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': 'z',
'center': [
157.5,
@ -1615,8 +1599,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': '4',
'center': [
-67.5,
@ -1695,8 +1678,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '5',
'center': [
-22.5,
@ -1775,8 +1757,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '3',
'center': [
-112.5,
@ -1847,16 +1828,14 @@ define(function () {
}
],
'properties': {
'label': 'Top 2 geo.dest: CN',
'length': 23,
'min': 1,
'max': 39,
'precision': 1
'max': 39
}
},
'label': 'Top 2 geo.dest: CN'
}
},
{
'label': 'Top 2 geo.dest: IN',
'valueFormatter': _.identity,
'geoJson': {
'type': 'FeatureCollection',
'features': [
@ -1870,8 +1849,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 31,
'value': 31,
'geohash': '6',
'center': [
-67.5,
@ -1950,8 +1928,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 30,
'value': 30,
'geohash': 's',
'center': [
22.5,
@ -2030,8 +2007,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 29,
'value': 29,
'geohash': 'w',
'center': [
112.5,
@ -2110,8 +2086,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 28,
'value': 28,
'geohash': 'd',
'center': [
-67.5,
@ -2190,8 +2165,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 25,
'value': 25,
'geohash': 't',
'center': [
67.5,
@ -2270,8 +2244,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 24,
'value': 24,
'geohash': 'k',
'center': [
22.5,
@ -2350,8 +2323,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 20,
'value': 20,
'geohash': 'u',
'center': [
22.5,
@ -2430,8 +2402,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 18,
'value': 18,
'geohash': '9',
'center': [
-112.5,
@ -2510,8 +2481,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 14,
'value': 14,
'geohash': 'v',
'center': [
67.5,
@ -2590,8 +2560,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 11,
'value': 11,
'geohash': 'e',
'center': [
-22.5,
@ -2670,8 +2639,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 9,
'value': 9,
'geohash': 'r',
'center': [
157.5,
@ -2750,8 +2718,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'y',
'center': [
112.5,
@ -2830,8 +2797,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 6,
'value': 6,
'geohash': 'f',
'center': [
-67.5,
@ -2910,8 +2876,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 5,
'value': 5,
'geohash': 'g',
'center': [
-22.5,
@ -2990,8 +2955,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 5,
'value': 5,
'geohash': 'c',
'center': [
-112.5,
@ -3070,8 +3034,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 4,
'value': 4,
'geohash': 'b',
'center': [
-157.5,
@ -3150,8 +3113,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 3,
'value': 3,
'geohash': 'q',
'center': [
112.5,
@ -3230,8 +3192,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 2,
'value': 2,
'geohash': '4',
'center': [
-67.5,
@ -3310,8 +3271,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'z',
'center': [
157.5,
@ -3390,8 +3350,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'x',
'center': [
157.5,
@ -3470,8 +3429,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'p',
'center': [
157.5,
@ -3550,8 +3508,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': 'm',
'center': [
67.5,
@ -3630,8 +3587,7 @@ define(function () {
]
},
'properties': {
'valueLabel': 'Count',
'count': 1,
'value': 1,
'geohash': '7',
'center': [
-22.5,
@ -3702,14 +3658,10 @@ define(function () {
}
],
'properties': {
'label': 'Top 2 geo.dest: IN',
'length': 23,
'min': 1,
'max': 31,
'precision': 1
'max': 31
}
},
'label': 'Top 2 geo.dest: IN'
}
}
],
'hits': 1639

View file

@ -65,16 +65,11 @@ define(function (require) {
var items = vis.handler.legend.labels;
items.forEach(function (label) {
var path = _(paths)
.map(function (path) {
return path.getAttribute('data-label');
})
.filter(function (dataLabel) {
return dataLabel === label;
})
.value();
var path = _.find(paths, function (path) {
return path.getAttribute('data-label') === String(label);
});
expect(path.length).to.be.greaterThan(0);
expect(path).to.be.ok();
});
});
});

View file

@ -11,23 +11,24 @@ define(function (require) {
var EventEmitter;
var checker;
var reflowWatcher;
var spyReflowOn;
var reflowSpies = {};
beforeEach(module('kibana'));
beforeEach(inject(function (Private) {
window.DISABLE_RESIZE_CHECKER = false;
ResizeChecker = Private(require('components/vislib/lib/resize_checker'));
EventEmitter = Private(require('factories/events'));
reflowWatcher = Private(require('components/reflow_watcher'));
reflowSpies.on = sinon.spy(reflowWatcher, 'on');
reflowSpies.off = sinon.spy(reflowWatcher, 'off');
spyReflowOn = sinon.spy(reflowWatcher, 'on');
checker = new ResizeChecker(
$(document.createElement('div'))
.appendTo('body')
.css('visibility', 'hidden')
.get(0)
);
spyReflowOn.restore();
var $el = $(document.createElement('div'))
.appendTo('body')
.css('visibility', 'hidden')
.get(0);
checker = new ResizeChecker($el);
}));
afterEach(function () {
@ -36,37 +37,34 @@ define(function (require) {
checker.destroy();
});
it('is an event emitter', function () {
expect(checker).to.be.a(EventEmitter);
});
it('emits a "resize" event when the el is resized', function (done) {
checker.on('resize', function () {
done();
describe('basic functionality', function () {
it('is an event emitter', function () {
expect(checker).to.be.a(EventEmitter);
});
checker.$el.text('haz contents');
checker.check();
});
it('listens for the "reflow" event of the reflowWatchers', function () {
expect(reflowSpies.on).to.have.property('callCount', 1);
var call = reflowSpies.on.getCall(0);
expect(call.args[0]).to.be('reflow');
});
it('listens for the "reflow" event of the reflowWatchers', function () {
expect(spyReflowOn).to.have.property('callCount', 1);
var call = spyReflowOn.getCall(0);
expect(call.args[0]).to.be('reflow');
it('emits a "resize" event when the el is resized', function (done) {
checker.on('resize', function () {
done();
});
checker.$el.text('haz contents');
checker.check();
});
});
describe('#read', function () {
it('uses jquery to get the width and height of the element', function () {
var stubw = sinon.spy($.fn, 'width');
var stubh = sinon.spy($.fn, 'height');
it('gets the proper dimensions for the element', function () {
var dimensions = checker.read();
var windowWidth = window.innerWidth;
checker.read();
expect(stubw).to.have.property('callCount', 1);
expect(stubw.getCall(0)).to.have.property('thisValue', checker.$el);
expect(stubh).to.have.property('callCount', 1);
expect(stubh.getCall(0)).to.have.property('thisValue', checker.$el);
expect(dimensions.w).to.equal(windowWidth);
expect(dimensions.h).to.equal(0);
});
});
@ -142,6 +140,23 @@ define(function (require) {
});
});
describe('#destroy()', function () {
it('removes the "reflow" event from the reflowWatcher', function () {
var onCall = reflowSpies.on.getCall(0);
var handler = onCall.args[1];
checker.destroy();
expect(reflowSpies.off).to.have.property('callCount', 1);
expect(reflowSpies.off.calledWith('reflow', handler)).to.be.ok();
});
it('clears the timeout', function () {
var spy = sinon.spy(window, 'clearTimeout');
checker.destroy();
expect(spy).to.have.property('callCount', 1);
});
});
describe('scheduling', function () {
var clock;
var schedule;
@ -182,20 +197,5 @@ define(function (require) {
});
});
});
describe('#destroy()', function () {
it('removes the "reflow" event from the reflowWatcher', function () {
var stub = sinon.stub(reflowWatcher, 'off');
checker.destroy();
expect(stub).to.have.property('callCount', 1);
expect(stub.calledWith('reflow')).to.be.ok();
});
it('clears the timeout', function () {
var spy = sinon.spy(window, 'clearTimeout');
checker.destroy();
expect(spy).to.have.property('callCount', 1);
});
});
});
});
});

View file

@ -161,7 +161,7 @@ define(function (require) {
it('should filter out data points that are outside of the map bounds', function () {
vis.handler.charts.forEach(function (chart) {
chart.maps.forEach(function (map) {
var featuresLength = chart.chartData.geoJson.features.length;
var featuresLength = chart.geoJson.features.length;
var mapFeatureLength;
function getSize(obj) {
@ -249,22 +249,22 @@ define(function (require) {
});
});
it('should return the min of all features.properties.count', function () {
it('should return the min of all features.properties.value', function () {
vis.handler.charts.forEach(function (chart) {
var data = chart.handler.data.data;
var min = _.chain(data.geoJson.features)
.deepPluck('properties.count')
.deepPluck('properties.value')
.min()
.value();
expect(chart.getMinMax(data).min).to.be(min);
});
});
it('should return the max of all features.properties.count', function () {
it('should return the max of all features.properties.value', function () {
vis.handler.charts.forEach(function (chart) {
var data = chart.handler.data.data;
var max = _.chain(data.geoJson.features)
.deepPluck('properties.count')
.deepPluck('properties.value')
.max()
.value();
expect(chart.getMinMax(data).max).to.be(max);
@ -275,13 +275,13 @@ define(function (require) {
describe('dataToHeatArray method', function () {
it('should return an array', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.dataToHeatArray(mapData, max)).to.be.an(Array);
expect(chart.dataToHeatArray(max)).to.be.an(Array);
});
});
it('should return an array item for each feature', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.dataToHeatArray(mapData, max).length).to.be(mapData.features.length);
expect(chart.dataToHeatArray(max).length).to.be(mapData.features.length);
});
});
@ -289,8 +289,8 @@ define(function (require) {
vis.handler.charts.forEach(function (chart) {
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var intensity = feature.properties.count;
var array = chart.dataToHeatArray(mapData, max);
var intensity = feature.properties.value;
var array = chart.dataToHeatArray(max);
expect(array[i][0]).to.be(lat);
expect(array[i][1]).to.be(lng);
expect(array[i][2]).to.be(intensity);
@ -302,8 +302,8 @@ define(function (require) {
chart._attr.heatNormalizeData = true;
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var intensity = parseInt(feature.properties.count / max * 100);
var array = chart.dataToHeatArray(mapData, max);
var intensity = parseInt(feature.properties.value / max * 100);
var array = chart.dataToHeatArray(max);
expect(array[i][0]).to.be(lat);
expect(array[i][1]).to.be(lng);
expect(array[i][2]).to.be(intensity);
@ -352,19 +352,19 @@ define(function (require) {
describe('nearestFeature method', function () {
it('should return an object', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData)).to.be.an(Object);
expect(chart.nearestFeature(point)).to.be.an(Object);
});
});
it('should return a geoJson feature', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData).type).to.be('Feature');
expect(chart.nearestFeature(point).type).to.be('Feature');
});
});
it('should return the geoJson feature with same latlng as point', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData)).to.be(feature);
expect(chart.nearestFeature(point)).to.be(feature);
});
});
});