removed visualize app

This commit is contained in:
Spencer Alger 2014-08-19 09:27:49 -07:00
parent 86e936d088
commit 42a3d5ba4b
45 changed files with 0 additions and 2302 deletions

View file

@ -10,14 +10,6 @@
- Move to utility class (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/discover/controllers/discover.js)
- **src/kibana/apps/settings/sections/indices/_create.js**
- we should probably display a message of some kind (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/settings/sections/indices/_create.js)
- **src/kibana/apps/visualize/saved_visualizations/_adhoc_vis.js**
- Should we abtract out the agg building stuff? (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/visualize/saved_visualizations/_adhoc_vis.js)
- Should this be abstracted somewhere? Its a copy/paste from _saved_vis.js (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/visualize/saved_visualizations/_adhoc_vis.js)
- **src/kibana/apps/visualize/saved_visualizations/_type_defs.js**
- We need to be able to get ahold of angular services here (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/visualize/saved_visualizations/_type_defs.js)
- We need to be able to get ahold of angular services here (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/visualize/saved_visualizations/_type_defs.js)
- **src/kibana/apps/visualize/saved_visualizations/bucket_aggs/terms.js**
- We need more just _count here. (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/visualize/saved_visualizations/bucket_aggs/terms.js)
- **src/kibana/components/agg_types/buckets/terms.js**
- We need more than just _count here. (https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/agg_types/buckets/terms.js)
- **src/kibana/components/index_patterns/_mapper.js**

View file

@ -1,97 +0,0 @@
<div ng-controller="VisualizeEditor" class="vis-editor">
<div ng-if="!appEmbedded" class="app-container">
<navbar>
<span ng-if="!!vis.title" class="name" ng-bind="vis.title"></span>
<div class="fill bitty-modal-container">
<div ng-if="linked && !unlinking"
ng-dblclick="unlink()"
tooltip="Double click to unlink this visualization from the saved search"
class="bitty-modal visualize-linked">
<i class="fa fa-link"></i>
&nbsp;
This visualization is linked to a saved search:
<b>{{ vis.savedSearchId | json}}</b>
<a href="#/discover/{{ vis.savedSearchId | uriescape }}">
<i class="fa fa-pencil" tooltip="Click here to edit the linked saved search"></i>
</a>
</div>
<div ng-if="linked && unlinking" ng-click="doneUnlinking()" class="bitty-modal">
<i class="fa fa-chain-broken"></i> Unlinked!
</div>
<form ng-submit="fetch()" class="inline-form" name="queryInput">
<div class="typeahead" kbn-typeahead="visualize">
<div class="input-group"
ng-class="queryInput.$invalid ? 'has-error' : ''">
<input query-input="vis.searchSource"
kbn-typeahead-input
placeholder="Search..."
type="text"
class="form-control"
ng-model="state.query">
<button class="btn btn-default" type="submit"
ng-disabled="queryInput.$invalid">
<span class="fa fa-search"></span>
</button>
</div>
<kbn-typeahead-items></kbn-typeahead-items>
</div>
</form>
</div>
<div class="button-group">
<button ng-click="startOver()"><i class="fa fa-file-o"></i></button>
<button ng-click="toggleSave()"><i class="fa fa-save"></i></button>
<button ng-click="toggleLoad()"><i class="fa fa-folder-open"></i></button>
<button ng-click="toggleShare()"><i class="fa fa-code"></i></button>
<button ng-click="fetch()"><i class="fa fa-refresh"></i></button>
</div>
</navbar>
<config
config-template="configTemplate"
config-object="conf">
</config>
<div class="vis-editor-content">
<div class="vis-sidebar">
<div class="sidebar-container">
<form class="sidebar-list" ng-submit="fetch()" name="visualizeEditor">
<ul class="list-unstyled">
<li ng-repeat="category in visConfigCategories.displayOrder" class="sidebar-item">
<vis-config-category
vis="vis"
category="vis[category.name]"
fields="fields">
</vis-config-category>
</li>
<li class="sidebar-item">
<button
ng-click="fetch()"
ng-if="stateDirty"
ng-disabled="httpActive.length || visualizeEditor.$invalid"
class="sidebar-item-button success">
Apply
</button>
</li>
</ul>
</form>
</div>
</div>
<div class="vis-canvas">
<vis-canvas><visualize vis="vis"></visualize></vis-canvas>
</div>
</div>
</div>
<div ng-if="appEmbedded" class="container-fluid">
<div class="row vis-editor-content">
<div class="vis-canvas">
<vis-canvas><visualize vis="vis"></visualize></vis-canvas>
</div>
</div>
</div>
</div>

View file

@ -1,16 +0,0 @@
define(function (require) {
require('css!apps/visualize/styles/main.css');
require('apps/visualize/controllers/editor');
require('apps/visualize/controllers/wizard');
require('apps/visualize/directives/canvas');
require('apps/visualize/directives/visualize');
require('apps/visualize/directives/config_category');
require('apps/visualize/directives/search_editor');
require('routes')
.when('/visualize', {
redirectTo: '/visualize/step/1'
});
});

View file

@ -1,24 +0,0 @@
<div
class="sidebar-item-title"
ng-if="category.name !== 'group' || vis.segment.configs.length > 0">
<span>
<button
type="button"
ng-if="category.configs.length < category.max"
ng-click="vis.addConfig(category.name)"
class="btn btn-xs btn-default" >
<i class="fa fa-plus"></i>
</button>
{{ category.label }}
</span>
</div>
<div ng-if="category.configs.length > 0" class="vis-config-details">
<vis-config-editor
ng-repeat="config in category.configs"
category="category"
config="config"
vis="vis"
fields="fields"
move="moveHandler">
</vis-config-editor>
</div>

View file

@ -1,22 +0,0 @@
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label>Min <small>(optional)</small></label>
<input
ng-show="aggParams.min_doc_count"
ng-model="config.extended_bounds.min"
type="number"
class="form-control"
name="extended_bounds.min" />
</div>
<div class="col-xs-6">
<label>Max <small>(optional)</small></label>
<input
ng-show="aggParams.min_doc_count"
ng-model="config.extended_bounds.max"
type="number"
class="form-control"
name="extended_bounds.max" />
</div>
</div>
</div>

View file

@ -1,26 +0,0 @@
<div ng-init="config.filters = (config.filters || [{input: {}}])">
<div class="form-group" >
<div ng-repeat="filter in config.filters track by $index">
<div class="config-controls pull-right btn-group">
<span ng-click="config.filters.splice($index, 1)"
class="btn btn-danger btn-xs ">
<i class="fa fa-ban" ></i>
</span>
</div>
<div class="form-group">
<label>Query string {{$index + 1}}</label>
<input query-input
ng-model="filter.input"
type="text"
class="form-control"
name="filter{{$index}}" />
</div>
</div>
</div>
</div>
<div
ng-click="config.filters.push({input: {}})"
class="sidebar-item-button primary">
Add filter
</div>
</div>

View file

@ -1,9 +0,0 @@
<div class="checkbox">
<label>
<input
ng-model="config.global"
type="checkbox">Run First
&nbsp;
<kbn-info info="Ensure that all charts have the same {{group.label | lowercase}} values." placement="right"></kbn-info>
</label>
</div>

View file

@ -1,22 +0,0 @@
<div class="form-group">
<table class="agg-config-interval">
<tr>
<td>
<label>Interval</label>
<select
ng-if="aggParams.interval.options"
ng-model="config.interval"
ng-options="opt.val as opt.display for opt in aggParams.interval.options"
class="form-control"
name="interval">
</select>
<input
ng-if="!aggParams.interval.options"
ng-model="config.interval"
type="number"
class="form-control"
name="interval" />
</td>
</tr>
</table>
</div>

View file

@ -1,43 +0,0 @@
<div ng-init="config.ranges = (config.ranges || [{}])">
<div class="form-group" >
<div class="row">
<div class="col-xs-5">
<label>From</label>
</div>
<div class="col-xs-5">
<label>To</label>
</div>
</div>
<div class="row"
ng-repeat="range in config.ranges track by $index">
<div class="col-xs-5">
<input validate-ip
ng-model="range.from"
type="text"
class="form-control"
name="range.from" />
</div>
<div class="col-xs-5">
<input validate-ip
ng-model="range.to"
type="text"
class="form-control"
name="range.to" />
</div>
<div class="col-xs-1">
<button ng-click="config.ranges.splice($index, 1)"
class="btn btn-danger btn-xs">
<i class="fa fa-ban" ></i>
</button>
</div>
</div>
</div>
<div
ng-click="config.ranges.push({})"
class="sidebar-item-button primary">
Add Range
</div>
</div>

View file

@ -1,16 +0,0 @@
<div class="form-group">
<table class="agg-config-interval">
<tr>
<td>
<div class="checkbox ng-scope">
<label>
<input ng-model="config.min_doc_count"
type="checkbox">Show empty buckets
&nbsp;
<kbn-info info="Show all buckets, not only the buckets with results." placement="right" class="ng-isolate-scope"></kbn-info>
</label>
</div>
</td>
</tr>
</table>
</div>

View file

@ -1,15 +0,0 @@
<div class="form-group">
<div class="row">
<div class="col-sm-6">
<select
class="form-control"
name="order"
ng-model="config.order"
ng-options="opt.val as opt.display for opt in aggParams.order.options">
</select>
</div>
<div class="col-sm-6">
<input class="form-control" type="number" ng-model="config.size" name="size">
</div>
</div>
</div>

View file

@ -1,43 +0,0 @@
<div ng-init="config.ranges = (config.ranges || [{}])">
<div class="form-group" >
<div class="row">
<div class="col-xs-5">
<label>From</label>
</div>
<div class="col-xs-5">
<label>To</label>
</div>
</div>
<div class="row"
ng-repeat="range in config.ranges track by $index">
<div class="col-xs-5">
<input
ng-model="range.from"
type="number"
class="form-control"
name="range.from" />
</div>
<div class="col-xs-5">
<input
ng-model="range.to"
type="number"
class="form-control"
name="range.to" />
</div>
<div class="col-xs-1">
<button ng-click="config.ranges.splice($index, 1)"
class="btn btn-danger btn-xs">
<i class="fa fa-ban" ></i>
</button>
</div>
</div>
</div>
<div
ng-click="config.ranges.push({})"
class="sidebar-item-button primary">
Add Range
</div>
</div>

View file

@ -1,39 +0,0 @@
<div ng-if="category.name === 'split'" class="form-group">
<div class="btn-group">
<button
type="button"
class="btn btn-xs btn-default"
ng-model="config.row"
btn-radio="true">
Row
</button>
<button
type="button"
class="btn btn-xs btn-default"
ng-model="config.row"
btn-radio="false">
Column
</button>
</div>
</div>
<div class="form-group" ng-show="availableAggs">
<label>Aggregation</label>
<select
name="agg"
class="form-control"
ng-model="config.agg"
ng-options="agg.name as agg.display for agg in availableAggs">
</select>
</div>
<div class="form-group">
<label for="field">
Field
</label>
<select
class="form-control"
name="field"
ng-model="config.field"
ng-options="field.name as field.name group by field.type for field in fields | filter:{indexed:true} |orderBy:['type','name']">
</select>
</div>
<div class="agg-param-controls"></div>

View file

@ -1,25 +0,0 @@
<div class="config-controls pull-right btn-group">
<button
ng-if="category.configs.length > 1"
ng-click="move(config, -1)"
type="button"
ng-disabled="category.configs.indexOf(config) < 1"
class="btn btn-xs btn-default">
<div tooltip="Increase Priority"><i class="fa fa-caret-up"></i></div>
</button>
<button
ng-if="category.configs.length > 1"
ng-click="move(config, 1)"
type="button"
ng-disabled="category.configs.indexOf(config) >= category.configs.length - 1"
class="btn btn-xs btn-default">
<div tooltip="Decrease Priority"><i class="fa fa-caret-down"></i></div>
</button>
<button
ng-if="category.configs.length > category.min"
ng-click="move(config, false)"
type="button"
class="btn btn-xs btn-danger">
<div tooltip="Remove Dimension"><i class="fa fa-times"></i></div>
</button>
</div>

View file

@ -1,21 +0,0 @@
<div class="form-group">
<label for="stat">Stat</label>
<select
class="form-control"
name="stat"
ng-model="config.agg"
ng-options="agg.name as agg.name for agg in aggs.metricAggs">
</select>
</div>
<div class="form-group" ng-if="config.agg && config.agg !== 'count'">
<label for="field">
Field to {{config.agg}}&nbsp;
<kbn-info placement="right" info="Field to use for the {{metric.label}}"></kbn-info>
</label>
<select
class="form-control"
name="field"
ng-model="config.field"
ng-options="field.name as field.name for field in fields | fieldType:aggs.metricAggsByName[config.agg].types">
</select>
</div>

View file

@ -1 +0,0 @@
<saved-object-finder type="visualizations"></saved-object-finder>

View file

@ -1,12 +0,0 @@
<form role="form" ng-submit="conf.doSave()">
<div class="form-group">
<label for="visTitle">Title</label>
<input class="form-control" type="text" name="visTitle" ng-model="conf.vis.title" required input-focus>
</div>
<div class="form-group">
<label for="visDescription">Description</label>
<textarea class="form-control" name="visDescription" ng-model="conf.vis.description" placeholder=""></textarea>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>

View file

@ -1,11 +0,0 @@
<form role="form" class="vis-share">
<div class="form-group">
<label>Embed this visualization. <small>Copy code into your html source. Note all clients must still be able to access kibana</small></label>
<div class="form-control" disabled>&lt;iframe src="{{conf.shareData().embed}}" height="400" width="600"&gt;&lt;/iframe&gt;</div>
</div>
<div class="form-group">
<label>Share a link</label>
<div class="form-control" disabled>{{conf.shareData().link}}</div>
</div>
</form>

View file

@ -1,8 +0,0 @@
<div ng-if="chartData.hits === 0"
class="visualize-error visualize-chart">
<h2><i class="fa fa-meh-o"></i></h2>
<h4>No results found</h4>
</div>
<div ng-hide="chartData.hits === 0" class="visualize-chart"></div>
<visualize-spy></visualize-spy>

View file

@ -1,131 +0,0 @@
define(function (require) {
var _ = require('lodash');
var module = require('modules').get('app/visualize');
var configCats = require('apps/visualize/saved_visualizations/_config_categories');
module.factory('AdhocVis', function (courier, Private, Promise) {
var aggs = Private(require('apps/visualize/saved_visualizations/_aggs'));
/**
opts params:
{
type: 'histogram', // The chart type
listeners : {
onClick: function,
onHover: function,
onBrush: function,
},
params: {}, // top level chart parameters
searchSource: SearchSource // the search source for the visualization
}
*/
function AdhocVis(opts) {
opts = opts || {};
if (!_.isObject(opts)) throw new TypeError('options must be an object');
var vis = this;
var params;
var createdSource = true;
vis.init = _.once(function () {
vis.typeName = opts.type || 'histogram';
vis.params = _.cloneDeep(opts.params);
// give vis the properties of config
_.assign(vis, opts.config);
// also give it the on* interaction functions, if any
_.assign(vis, opts.listeners);
vis._fillConfigsToMinimum();
// resolve the search source for this AdhocVis
return Promise.cast((function () {
if (opts.searchSource) {
// did not create the source, so we won't destroy it either
createdSource = false;
return opts.searchSource;
}
return courier.createSource('search');
}()))
.then(function (searchSource) {
// TODO: Should we abtract out the agg building stuff?
searchSource.aggs(function () {
// stores the config objects in queryDsl
var dsl = {};
// counter to ensure unique agg names
var i = 0;
// start at the root, but the current will move
var current = dsl;
// continue to nest the aggs under each other
// writes to the dsl object
vis.getConfig().forEach(function (config) {
current.aggs = {};
var key = '_agg_' + (i++);
var aggDsl = {};
aggDsl[config.agg] = config.aggParams;
current = current.aggs[key] = aggDsl;
});
// set the dsl to the searchSource
return dsl.aggs || {};
});
vis.searchSource = searchSource;
return vis;
});
});
// TODO: Should this be abstracted somewhere? Its a copy/paste from _saved_vis.js
vis._fillConfigsToMinimum = function () {
// satify the min count for each category
configCats.fetchOrder.forEach(function (category) {
var myCat = vis[category.name];
if (myCat.configs.length < myCat.min) {
_.times(myCat.min - myCat.configs.length, function () {
vis.addConfig(category.name);
});
}
});
};
vis.destroy = function () {
if (createdSource) {
this.searchSource.cancelPending();
} else {
//remove our aggregations from the serarch source
this.searchSource.set('aggs', null);
}
};
/**
* Create a list of config objects, which are ready to be turned into aggregations,
* in the order which they should be executed.
*
* @return {Array} - The list of config objects
*/
vis.getConfig = Private(require('apps/visualize/saved_visualizations/_read_config'));
/**
* Transform an ES Response into data for this visualization
* @param {object} resp The elasticsearch response
* @return {array} An array of flattened response rows
*/
vis.buildChartDataFromResponse = Private(require('apps/visualize/saved_visualizations/_build_chart_data'));
}
return AdhocVis;
});
});

View file

@ -1,103 +0,0 @@
define(function (require) {
return function AggsService(Private) {
require('lodash');
var _ = require('lodash');
var aggs = {};
aggs.metricAggs = [
{
name: 'count',
display: 'Count',
types: ['number'],
makeLabel: function (params) {
return 'Count of documents';
}
},
{
name: 'avg',
display: 'Average',
types: ['number'],
makeLabel: function (params) {
return 'Average ' + params.field;
}
},
{
name: 'sum',
display: 'Sum',
types: ['number'],
makeLabel: function (params) {
return 'Sum of ' + params.field;
}
},
{
name: 'min',
display: 'Min',
types: ['number'],
makeLabel: function (params) {
return 'Min ' + params.field;
}
},
{
name: 'max',
display: 'Max',
types: ['number'],
makeLabel: function (params) {
return 'Max ' + params.field;
}
},
{
name: 'cardinality',
display: 'Unique count',
types: ['*'],
makeLabel: function (params) {
return 'Unique count of ' + params.field;
}
},
];
aggs.metricAggsByName = _.indexBy(aggs.metricAggs, 'name');
aggs.bucketAggs = Private(require('apps/visualize/saved_visualizations/bucket_aggs/_index'));
aggs.bucketAggsByName = _.indexBy(aggs.bucketAggs, 'name');
aggs.byName = _.assign({}, aggs.bucketAggsByName, aggs.metricAggsByName);
aggs.byFieldType = {
number: [
aggs.bucketAggsByName.terms,
aggs.bucketAggsByName.histogram,
aggs.bucketAggsByName.range,
// 'range'
],
date: [
// 'date range',
aggs.bucketAggsByName.date_histogram,
aggs.bucketAggsByName.terms,
],
boolean: [
aggs.bucketAggsByName.terms,
// 'terms'
],
ip: [
aggs.bucketAggsByName.terms,
aggs.bucketAggsByName.ip_range,
// 'ipv4 range'
],
geo_point: [
aggs.bucketAggsByName.terms,
// 'geo distance'
],
geo_shape: [
aggs.bucketAggsByName.terms,
// 'geohash grid'
],
string: [
// 'significant terms',
aggs.bucketAggsByName.terms,
// 'range'
]
};
return aggs;
};
});

View file

@ -1,53 +0,0 @@
define(function (require) {
var _ = require('lodash');
var categories = [
{
name: 'segment',
displayOrder: 2,
fetchOrder: 1,
min: 0,
max: Infinity,
configDefaults: {
size: 5
}
},
{
name: 'metric',
displayOrder: 1,
fetchOrder: 2,
min: 0,
max: 1,
configDefaults: {
agg: 'count'
}
},
{
name: 'group',
displayOrder: 3,
fetchOrder: 3,
min: 0,
max: 1,
configDefaults: {
global: false,
size: 5
}
},
{
name: 'split',
displayOrder: 4,
fetchOrder: 4,
min: 0,
max: 2,
configDefaults: {
size: 5,
row: true
}
}
];
categories.fetchOrder = _.sortBy(categories, 'fetchOrder');
categories.displayOrder = _.sortBy(categories, 'displayOrder');
categories.byName = _.indexBy(categories, 'name');
return categories;
});

View file

@ -1,111 +0,0 @@
define(function (require) {
return function ReadConfigFn(Private, $injector) {
var _ = require('lodash');
var configCategories = require('apps/visualize/saved_visualizations/_config_categories');
var aggs = Private(require('apps/visualize/saved_visualizations/_aggs'));
var courier = require('components/courier/courier');
return function readConfig() {
var vis = this;
// these arrays represent the different sections used to create an aggregation, and when config objects are encountered
// the are pushed into these array's based on their properties. Array's are used to make the logic and the final
// combination simple. Many of these will be limited to a single value by the UI
var positions = {
// used to create rows/columns
split: [],
// global segments (eg. color, marked in the ui to be applied gloabally and the same values should be used across all charts)
global: [],
// primary segments (eg. x-axis)
segment: [],
// local segments (eg. color, marked in the ui that it should apply within each chart)
local: [],
// metric is the root "measurement" (eg. y-axis)
metric: []
};
function moveValidatedParam(input, output, paramDef, name) {
if (!input[name]) {
if (paramDef.default != null) input[name] = _.cloneDeep(paramDef.default);
else return !paramDef.required;
}
var val = input[name];
var selectedOption = paramDef.options && _.find(paramDef.options, { val: val });
if (!paramDef.custom && paramDef.options && !selectedOption) return false;
if (paramDef.write) {
var selection = selectedOption;
// either the value is custom or there just aren't any options defined
if (!selectedOption && val != null) selection = { val: val };
// provide a hook to apply custom logic when writing this config value
paramDef.write(selection, output);
} else {
// copy over the param
output.aggParams[name] = val;
}
return true;
}
function makeCategoryValidator(category) {
return function categoryValidator(config) {
// filter out plain unusable configs
if (!config || !config.agg || !config.field) return;
// get the agg used by this config
var agg = aggs.byName[config.agg];
if (!agg || agg.name === 'count') return;
// copy parts of the config to the validated "output" object
var output = {
agg: config.agg,
aggParams: {},
categoryName: category.name
};
if (agg.name !== 'filters') output.aggParams.field = config.field;
// copy over other properties based on the category
switch (category.name) {
case 'split':
output.row = !!config.row;
break;
case 'group':
output.global = !!config.global;
break;
}
// this function will move valus from config.* to output.aggParams.* when they are
// needed for that aggregation, and return true or false based on if all requirements
// are meet
var moveToAggParams = _.partial(moveValidatedParam, config, output);
// ensure that all of the declared params for the agg are declared on the config
if (_.every(agg.params, moveToAggParams)) return output;
};
}
// collect all of the configs from each category,
// validate them, filter the invalid ones, and put them into positions
configCategories.fetchOrder.forEach(function (category) {
var configs = vis[category.name].configs;
configs = configs
.map(makeCategoryValidator(category))
.filter(Boolean);
if (category.name === 'group') {
positions.global = _.where(configs, { global: true });
positions.local = _.where(configs, { global: false });
} else {
positions[category.name] = configs;
}
});
// join all of the different positions into a single array
return positions.global.concat(positions.split, positions.segment, positions.local, positions.metric);
};
};
});

View file

@ -1,206 +0,0 @@
define(function (require) {
var _ = require('lodash');
var inherits = require('lodash').inherits;
var configCats = require('apps/visualize/saved_visualizations/_config_categories');
var typeDefs = require('apps/visualize/saved_visualizations/_type_defs');
var module = require('modules').get('app/visualize');
module.factory('SavedVis', function (config, $injector, courier, indexPatterns, Promise, savedSearches, Private) {
var aggs = Private(require('apps/visualize/saved_visualizations/_aggs'));
function SavedVis(opts) {
var vis = this;
opts = opts || {};
if (typeof opts !== 'object') {
opts = {
id: opts
};
}
var defaultParent = opts.parentSearchSource;
courier.SavedObject.call(vis, {
type: 'visualization',
id: opts.id,
mapping: {
title: 'string',
typeName: 'string',
stateJSON: 'string',
description: 'string',
savedSearchId: 'string',
indexPattern: 'string'
},
defaults: {
title: '',
typeName: opts.type || 'histogram',
stateJSON: null,
description: '',
savedSearchId: opts.savedSearchId,
indexPattern: opts.indexPattern
},
searchSource: true,
afterESResp: function setVisState() {
if (!vis.typeDef || vis.typeName !== vis.typeDef.name) {
// refresh the typeDef
vis.typeDef = typeDefs.byName[vis.typeName];
// refresh the defaults for all config categories
configCats.forEach(function (category) {
vis._initConfigCategory(category, vis[category.name]);
});
}
// get the saved state
var state;
if (vis.stateJSON) try { state = JSON.parse(vis.stateJSON); } catch (e) {}
// set the state on the vis
if (state) vis.setState(state);
var relatedSearch = vis.savedSearchId;
var relatedPattern = !relatedSearch && vis.indexPattern;
var promisedParent = (function () {
if (relatedSearch) {
// returns a promise
return savedSearches.get(vis.savedSearchId);
}
var fakeSavedSearch = {
searchSource: courier.createSource('search')
};
if (relatedPattern) {
return indexPatterns.get(relatedPattern)
.then(function (indexPattern) {
fakeSavedSearch.searchSource.index(indexPattern);
return fakeSavedSearch;
});
}
return Promise.resolve(fakeSavedSearch);
}());
return promisedParent
.then(function (parent) {
vis.savedSearch = parent;
vis.searchSource
.inherits(parent.searchSource)
.size(0)
// reads the vis' config and write the agg to the searchSource
.aggs(function () {
// stores the config objects in queryDsl
var dsl = {};
// counter to ensure unique agg names
var i = 0;
// start at the root, but the current will move
var current = dsl;
// continue to nest the aggs under each other
// writes to the dsl object
vis.getConfig().forEach(function (config) {
current.aggs = {};
var key = '_agg_' + (i++);
var aggDsl = {};
aggDsl[config.agg] = config.aggParams;
current = current.aggs[key] = aggDsl;
});
// set the dsl to the searchSource
return dsl.aggs || {};
});
vis._fillConfigsToMinimum();
_.assign(vis, vis.typeDef.listeners);
return vis;
});
}
});
vis.addConfig = function (categoryName) {
var category = configCats.byName[categoryName];
var config = _.defaults({}, category.configDefaults);
vis[category.name].configs.push(config);
return config;
};
vis.removeConfig = function (config) {
if (!config) return;
configCats.forEach(function (category) {
_.pull(vis[category.name].configs, config);
});
};
vis._fillConfigsToMinimum = function () {
// satify the min count for each category
configCats.fetchOrder.forEach(function (category) {
var myCat = vis[category.name];
if (myCat.configs.length < myCat.min) {
_.times(myCat.min - myCat.configs.length, function () {
vis.addConfig(category.name);
});
}
});
};
// init the config category, optionally pass in an existing category to refresh
// it's defaults based on the
vis._initConfigCategory = function (category, cat) {
cat = cat || {};
if (vis.typeDef) _.assign(cat, category, vis.typeDef.config[category.name]);
cat.configDefaults = _.clone(category.configDefaults),
cat.configs = cat.config || [];
vis[category.name] = cat;
return cat;
};
vis.setState = function (state) {
configCats.forEach(function (category) {
var categoryStates = state[category.name] || [];
vis[category.name].configs.splice(0);
categoryStates.forEach(function (configState) {
var config = vis.addConfig(category.name);
_.assign(config, configState);
});
});
vis._fillConfigsToMinimum();
};
vis.getState = function () {
return _.transform(configCats, function (state, category) {
var configs = state[category.name] = [];
[].push.apply(configs, vis[category.name].configs.map(function (config) {
return _.pick(config, function (val, key) {
return key.substring(0, 2) !== '$$';
});
}));
}, {});
};
}
inherits(SavedVis, courier.SavedObject);
return SavedVis;
});
});

View file

@ -1,153 +0,0 @@
define(function (require) {
var module = require('modules').get('apps/visualize');
var _ = require('lodash');
var typeDefs = [
{
name: 'histogram',
icon: 'icon-chart-bar',
params: {
shareYAxis: true,
addTooltip: true,
addLegend: true
},
listeners: {
onClick: function (e) {
// TODO: We need to be able to get ahold of angular services here
console.log(e);
}
},
config: {
metric: {
label: 'Y-Axis',
min: 1,
max: 1
},
segment: {
label: 'X-Axis',
min: 1,
max: 1
},
group: {
label: 'Color',
min: 0,
max: 1
},
split: {
label: 'Rows & Columns',
min: 0,
max: 1
}
}
},
{
name: 'line',
icon: 'icon-chart-bar',
params: {
shareYAxis: true,
addTooltip: true,
addLegend: true
},
listeners: {
},
config: {
metric: {
label: 'Y-Axis',
min: 1,
max: 1
},
segment: {
// limitToOrderedAggs: true,
label: 'X-Axis',
min: 1,
max: 1
},
group: {
label: 'Color',
min: 0,
max: 1
},
split: {
label: 'Rows & Columns',
min: 0,
max: 1
}
}
},
{
name: 'area',
icon: 'icon-chart-bar',
params: {
shareYAxis: true,
addTooltip: true,
addLegend: true,
isStacked: true
},
listeners: {
onClick: function (e) {
// TODO: We need to be able to get ahold of angular services here
console.log(e);
}
},
config: {
metric: {
label: 'Y-Axis',
min: 1,
max: 1
},
segment: {
// limitToOrderedAggs: true,
label: 'X-Axis',
min: 1,
max: 1
},
group: {
label: 'Color',
min: 0,
max: 1
},
split: {
label: 'Rows & Columns',
min: 0,
max: 1
}
}
},
{
name: 'pie',
icon: 'icon-chart-bar',
params: {
addTooltip: true,
addLegend: true
},
listeners: {
},
config: {
metric: {
label: 'Y-Axis',
min: 1,
max: 1
},
segment: {
label: 'X-Axis',
min: 1,
max: 1
},
group: {
label: 'Color',
min: 0,
max: 1
},
split: {
label: 'Rows & Columns',
min: 0,
max: 1
}
}
}
];
typeDefs.byName = _.indexBy(typeDefs, 'name');
return typeDefs;
});

View file

@ -1,13 +0,0 @@
define(function (require) {
return function AggsService(Private) {
return [
Private(require('apps/visualize/saved_visualizations/bucket_aggs/date_histogram')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/histogram')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/range')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/ip_range')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/terms')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/filters')),
Private(require('apps/visualize/saved_visualizations/bucket_aggs/significant_terms'))
];
};
});

View file

@ -1,119 +0,0 @@
define(function (require) {
return function DateHistogramAggDefinition(timefilter, config) {
var _ = require('lodash');
var moment = require('moment');
var interval = require('utils/interval');
// shorthand
var ms = function (type) { return moment.duration(1, type).asMilliseconds(); };
var pickInterval = function (bounds, targetBuckets) {
bounds || (bounds = timefilter.getBounds());
return interval.calculate(bounds.min, bounds.max, targetBuckets);
};
var agg = this;
agg.name = 'date_histogram';
agg.display = 'Date Histogram';
agg.ordered = {date: true};
agg.makeLabel = function (params, fullConfig) {
if (fullConfig.metricScaleText) return params.field + ' per ' + fullConfig.metricScaleText;
var aggInterval = _.find(agg.params.interval.options, { ms: interval.toMs(params.interval) });
if (aggInterval) return aggInterval.display + ' ' + params.field;
else return params.field + ' per ' + interval.describe(params.interval);
};
agg.params = {};
agg.params.interval = {
required: true,
default: 'auto',
custom: true,
options: [
{
display: 'Auto',
val: 'auto'
},
{
display: 'Second',
val: 'second',
ms: ms('second')
},
{
display: 'Minute',
val: 'minute',
ms: ms('minute')
},
{
display: 'Hourly',
val: 'hour',
ms: ms('hour')
},
{
display: 'Daily',
val: 'day',
ms: ms('day')
},
{
display: 'Weekly',
val: 'week',
ms: ms('week')
},
{
display: 'Monthly',
val: 'month',
ms: ms('month')
},
{
display: 'Yearly',
val: 'year',
ms: ms('year')
}
],
write: function (selection, output) {
var bounds = timefilter.getBounds();
var auto;
if (selection.val === 'auto') {
var bucketTarget = config.get('histogram:barTarget');
auto = pickInterval(bounds, bucketTarget);
output.aggParams.interval = auto.interval + 'ms';
output.metricScaleText = auto.description;
return;
}
var ms = selection.ms || interval.toMs(selection.val);
var buckets = Math.ceil((bounds.max - bounds.min) / ms);
var maxBuckets = config.get('histogram:maxBars');
if (buckets > maxBuckets) {
// we should round these buckets out, and scale back the y values
auto = pickInterval(bounds, maxBuckets);
output.aggParams.interval = auto.interval + 'ms';
output.metricScale = ms / auto.interval;
output.metricScaleText = selection.val || auto.description;
} else {
output.aggParams.interval = selection.val;
}
}
};
agg.params.format = {
hide: true,
custom: true
};
agg.params.extended_bounds = {
hide: true,
default: {},
write: function (selection, output) {
var bounds = timefilter.getBounds();
output.aggParams.extended_bounds = {
min: bounds.min,
max: bounds.max
};
}
};
};
});

View file

@ -1,44 +0,0 @@
define(function (require) {
return function HistogramAggDefinition(timefilter, config) {
var _ = require('lodash');
var moment = require('moment');
var agg = this;
agg.name = 'histogram';
agg.display = 'Histogram';
agg.ordered = {};
agg.makeLabel = function (params) {
return params.field;
};
agg.params = {};
agg.params.interval = {
required: true,
write: function (input, output) {
output.aggParams.interval = parseInt(input.val, 10);
}
};
agg.params.min_doc_count = {
custom: true,
default: false,
write: function (input, output) {
if (input.val) output.aggParams.min_doc_count = 0;
else delete output.aggParams.min_doc_count;
}
};
agg.params.extended_bounds = {
default: {},
write: function (input, output) {
output.aggParams.extended_bounds = {
min: input.val.min,
max: input.val.max
};
}
};
};
});

View file

@ -1,31 +0,0 @@
define(function (require) {
require('directives/validate_ip');
return function RangeAggDefinition(timefilter, config) {
var _ = require('lodash');
var moment = require('moment');
var angular = require('angular');
var agg = this;
agg.name = 'ip_range';
agg.display = 'IP Range';
//agg.ordered = {};
agg.makeLabel = function (params) {
return params.field;
};
agg.params = {};
agg.params.ranges = {
custom: true,
default: [{from: '0.0.0.0', to: '255.255.255.255'}],
write: function (input, output) {
output.aggParams.ranges = input.val;
output.aggParams.keyed = true;
}
};
};
});

View file

@ -1,29 +0,0 @@
define(function (require) {
return function RangeAggDefinition(timefilter, config) {
var _ = require('lodash');
var moment = require('moment');
var angular = require('angular');
var agg = this;
agg.name = 'range';
agg.display = 'Range';
//agg.ordered = {};
agg.makeLabel = function (params) {
return params.field;
};
agg.params = {};
agg.params.ranges = {
custom: true,
default: [{from: 0, to: 1000}, {from: 1000, to: 2000}],
write: function (input, output) {
output.aggParams.ranges = input.val;
output.aggParams.keyed = true;
}
};
};
});

View file

@ -1,19 +0,0 @@
define(function (require) {
return function SignificantTermsAggDefinition() {
var _ = require('lodash');
var agg = this;
agg.name = 'significant_terms';
agg.display = 'Significant Terms';
agg.makeLabel = function (params) {
return 'Top ' + params.size + ' unusual terms in ' + params.field;
};
agg.params = {
size: {
required: false,
}
};
};
});

View file

@ -1,32 +0,0 @@
define(function (require) {
return function TermsAggDefinition() {
var _ = require('lodash');
var agg = this;
agg.name = 'terms';
agg.display = 'Terms';
agg.makeLabel = function (params) {
var order = _.find(agg.params.order.options, { val: params.order._count });
return order.display + ' ' + params.size + ' ' + params.field;
};
agg.params = {
size: {
required: false,
},
order: {
required: true,
options: [
{ display: 'Top', val: 'desc' },
{ display: 'Bottom', val: 'asc' }
],
default: 'desc',
write: function (selection, output) {
// TODO: We need more just _count here.
output.aggParams.order = { _count: selection.val };
}
}
};
};
});

View file

@ -1,38 +0,0 @@
define(function (require) {
return function ConfigGroupFactory() {
var _ = require('lodash');
function ConfigGroup(def, visTypeDef) {
this.def = def;
this.list = [];
this.name = this.def.name;
this.setTypeDef(visTypeDef);
}
// /**
// * Update properties that can be overridden by the typeDef
// *
// * @param {visTypeDef} typeDef
// */
// ConfigGroup.prototype.setTypeDef = function (typeDef) {
// var merged = _.merge({},
// typeDef.configGroupDefs.byName[this.def.name],
// this.def
// );
// this.min = merged.min || 0;
// this.max = merged.max || Infinity;
// this.label = merged.label || '';
// };
ConfigGroup.prototype.add =
ConfigGroup.prototype.move =
ConfigGroup.prototype.remove = function (config) {
throw new Error('not implemented');
};
return ConfigGroup;
};
});

View file

@ -1,138 +0,0 @@
define(function (require) {
return function HistogramConverterFn(Private, timefilter) {
var _ = require('lodash');
var moment = require('moment');
var aggs = Private(require('apps/visualize/saved_visualizations/_aggs'));
var interval = require('utils/interval');
return function (chart, columns, rows) {
// index of color
var iColor = _.findIndex(columns, { categoryName: 'group' });
var hasColor = iColor !== -1;
/*****
* Get values related to the X-Axis
*****/
// index of the x-axis column
var iX = _.findIndex(columns, { categoryName: 'segment'});
// when we don't have an x-axis, just push everything into '_all'
if (iX === -1) {
iX = columns.push({
label: ''
}) - 1;
}
// column that defines the x-axis
var colX = columns[iX];
// aggregation for the x-axis
var aggX = colX.agg && aggs.byName[colX.agg];
/*****
* Get values related to the X-Axis
*****/
// index of y-axis stuff
var iY = _.findIndex(columns, { categoryName: 'metric'});
// column for the y-axis
var colY = columns[iY];
/*****
* Build the chart
*****/
// X-axis description
chart.xAxisLabel = colX.label;
if (aggX && aggX.ordered && aggX.ordered.date) {
chart.xAxisFormatter = (function () {
var bounds = timefilter.getBounds();
var format = interval.calculate(
moment(bounds.min.valueOf()),
moment(bounds.max.valueOf()),
rows.length
).format;
return function (thing) {
return moment(thing).format(format);
};
}());
var timeBounds = timefilter.getBounds();
chart.ordered = {
date: true,
min: timeBounds.min.valueOf(),
max: timeBounds.max.valueOf(),
interval: interval.toMs(colX.aggParams.interval)
};
}
else {
chart.xAxisFormatter = colX.field && colX.field.format.convert;
chart.ordered = aggX && aggX.ordered && {};
if (aggX !== false && colX && colX.aggParams && colX.aggParams.interval) {
chart.ordered.interval = colX.aggParams.interval;
}
}
// Y-axis description
chart.yAxisLabel = colY.label;
if (colY.field) chart.yAxisFormatter = colY.field.format.convert;
// setup the formatter for the label
chart.tooltipFormatter = function (datapoint) {
var datum = _.clone(datapoint);
if (colX.field) datum.x = colX.field.format.convert(datum.x);
if (colY.field) datum.y = colY.field.format.convert(datum.y);
if (colX.metricScaleText) {
datum.y += ' per ' + colX.metricScaleText;
}
var out = datum.label ? datum.label + '\n' : '';
out += datum.x + '\n';
out += datum.y;
return out;
};
var series = chart.series = [];
var seriesByLabel = {};
rows.forEach(function (row) {
var seriesLabel = hasColor && row[iColor];
var s = hasColor ? seriesByLabel[seriesLabel] : series[0];
if (!s) {
// I know this could be simplified but I wanted to keep the key order
if (hasColor) {
s = {
label: seriesLabel,
values: []
};
seriesByLabel[seriesLabel] = s;
} else {
s = {
values: []
};
}
series.push(s);
}
var datum = {
x: (row[iX] == null) ? '_all' : row[iX],
y: row[iY === -1 ? row.length - 1 : iY] // y-axis value
};
if (colX.metricScale) {
// support scaling response values to represent an average value on the y-axis
datum.y = datum.y * colX.metricScale;
}
s.values.push(datum);
});
};
};
});

View file

@ -1,11 +0,0 @@
define(function (require) {
return function RespConvertersService(Private) {
var histogram = Private(require('apps/visualize/saved_visualizations/resp_converters/histogram'));
return {
histogram: histogram,
line: histogram,
area: histogram,
pie: histogram
};
};
});

View file

@ -1,64 +0,0 @@
define(function (require) {
var app = require('modules').get('apps/visualize');
var typeDefs = require('apps/visualize/saved_visualizations/_type_defs');
var _ = require('lodash');
require('apps/visualize/saved_visualizations/_saved_vis');
// Register this service with the saved object registry so it can be
// edited by the object editor.
require('apps/settings/saved_object_registry').register({
service: 'savedVisualizations',
title: 'visualizations'
});
app.service('savedVisualizations', function (Promise, es, config, SavedVis) {
this.get = function (id) {
return (new SavedVis(id)).init();
};
this.urlFor = function (id) {
return '#/visualize/edit/' + encodeURIComponent(id);
};
this.delete = function (ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, function (id) {
return (new SavedVis(id)).delete();
});
};
this.find = function (searchString) {
var self = this;
var body = searchString ? {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
}
}
}: { query: {match_all: {}}};
return es.search({
index: config.file.kibanaIndex,
type: 'visualization',
body: body,
size: 100,
})
.then(function (resp) {
return {
total: resp.hits.total,
hits: resp.hits.hits.map(function (hit) {
var source = hit._source;
source.id = hit._id;
source.url = self.urlFor(hit._id);
source.typeDef = typeDefs.byName[source.typeName];
source.icon = source.typeDef.icon;
return source;
})
};
});
};
});
});

View file

@ -1,16 +0,0 @@
<div ng-show="spyMode.name === 'request'">
<pre>{{history[0].req | json}}</pre>
</div>
<div ng-show="spyMode.name === 'response'">
<pre>{{history[0].resp | json}}</pre>
</div>
<div ng-show="spyMode.name === 'stats'">
<table class="table">
<tr ng-repeat="pair in history[0].meta">
<td>{{pair[0]}}</td>
<td>{{pair[1]}}</td>
</tr>
</table>
</div>

View file

@ -1,56 +0,0 @@
define(function (require) {
return function VisSpyReqRespStats() {
var reqRespStatsHTML = require('text!apps/visualize/spy/_req_resp_stats.html');
var linkReqRespStats = function ($scope, config) {
$scope.$watchCollection('vis.searchSource.history', function (searchHistory) {
if (!searchHistory) {
$scope.history = [];
return;
}
$scope.history = searchHistory.map(function (entry) {
if (!entry.complete || !entry.state) return;
var state = entry.state;
var resp = entry.resp;
var meta = [];
if (resp && resp.took != null) meta.push(['Query Duration', resp.took + 'ms']);
if (entry && entry.ms != null) meta.push(['Request Duration', entry.ms + 'ms']);
if (resp && resp.hits) meta.push(['Hits', resp.hits.total]);
if (state.index) meta.push(['Index', state.index]);
if (state.type) meta.push(['Type', state.type]);
if (state.id) meta.push(['Id', state.id]);
return {
meta: meta,
req: state.body,
resp: entry.resp
};
}).filter(Boolean);
});
};
return [
{
name: 'request',
display: 'Request',
template: reqRespStatsHTML,
link: linkReqRespStats
},
{
name: 'response',
display: 'Response',
template: reqRespStatsHTML,
link: linkReqRespStats
},
{
name: 'stats',
display: 'Statistics',
template: reqRespStatsHTML,
link: linkReqRespStats
}
];
};
});

View file

@ -1,12 +0,0 @@
<div ng-click="toggleDisplay()" class="visualize-show-spy">
<small>
<i class="fa" ng-class="spyMode ? 'fa-chevron-down' : 'fa-chevron-up'"></i>
</small>
</div>
<ul ng-if="spyMode" class="nav nav-tabs visualize-spy-tabs">
<li
ng-repeat="mode in modes"
ng-class="{ active: spyMode.name === mode.name }">
<a ng-click="setSpyMode(mode)">{{mode.display}}</a>
</li>
</ul>

View file

@ -1,26 +0,0 @@
<paginate list="rows" per-page="10">
<table class="table table-condensed visualize-table">
<thead>
<tr bindonce>
<th
ng-repeat="col in columns track by $index"
ng-click="cycleSort(col)"
ng-class="getColumnClass(columns, $first, $last)">
<span bo-text="col"></span>
<i
class="fa"
ng-class="{
'fa-sort-asc': sort.field === col && sort.asc === true,
'fa-sort-desc': sort.field === col && sort.asc === false,
'fa-sort': !sort || sort.field !== col
}">
</i>
</th>
</tr>
</thead>
<tbody kbn-rows="page" kbn-rows-min="paginate.perPage"></tbody>
</table>
<a class="small" ng-click="exportAsCsv()">
Export <i class="fa fa-download"></i>
</a>
</paginate>

View file

@ -1,137 +0,0 @@
define(function (require) {
var module = require('modules').get('app/visualize');
var _ = require('lodash');
var saveAs = require('file_saver');
return function VisSpyTable(Notifier, $filter, $rootScope, config) {
return {
name: 'table',
display: 'Table',
template: require('text!apps/visualize/spy/_table.html'),
link: function tableLinkFn($scope, $el) {
var notify = new Notifier();
var orderBy = $filter('orderBy');
$scope.sort = null;
$scope.csv = {
showOptions: false,
separator: config.get('csv:separator'),
quoteValues: config.get('csv:quoteValues'),
filename: 'table.csv'
};
$scope.getColumnClass = function (col, $first, $last) {
var cls = [];
if ($last || $scope.fields && $scope.fields[col] && $scope.fields[col].type === 'number') {
cls.push('visualize-table-right');
}
if (!$scope.sort || $scope.sort.field !== col) {
cls.push('no-sort');
}
return cls.join(' ');
};
$scope.cycleSort = function (col) {
if (!$scope.sort || $scope.sort.field !== col) {
$scope.sort = {
field: col,
asc: true
};
} else if ($scope.sort.asc) {
$scope.sort.asc = false;
} else {
delete $scope.sort;
}
if ($scope.sort && !$scope.sort.getter) {
var fieldi = $scope.columns.indexOf($scope.sort.field);
$scope.sort.getter = function (row) {
return row[fieldi];
};
if (fieldi === -1) delete $scope.sort;
}
};
$scope.exportAsCsv = function () {
$scope.csv.showOptions = false;
if (!$scope.chartData) return;
var text = '';
var nonAlphaNumRE = /[^a-zA-Z0-9]/;
var allDoubleQuoteRE = /"/g;
var escape = function (val) {
val = String(val);
if ($scope.csv.quoteValues && nonAlphaNumRE.test(val)) {
val = '"' + val.replace(allDoubleQuoteRE, '""') + '"';
}
return val;
};
var raw = $scope.chartData.raw;
var rows = new Array(raw.rows.length + 1);
var colRow = [];
rows[0] = colRow;
raw.columns.forEach(function (col) {
colRow.push(escape(col.aggParams ? col.aggParams.field : 'count'));
});
raw.rows.forEach(function (rawRow, i) {
var row = new Array(rawRow.length);
rows[i + 1] = row;
rawRow.forEach(function (cell, i) {
row[i] = escape(cell);
});
});
var blob = new Blob(rows.map(function (row) {
return row.join($scope.csv.separator) + '\r\n';
}), { type: 'text/plain' });
saveAs(blob, $scope.csv.filename);
};
$rootScope.$watchMulti.call($scope, [
'chartData',
'sort.asc',
'sort.field'
], function () {
$scope.rows = null;
$scope.columns = null;
if (!$scope.chartData) return;
notify.event('flatten data for table', function () {
// flatten the fields to a list of strings
$scope.columns = [];
// collect the formatter for each column, in order
var formats = [];
// populate columns and formates
$scope.chartData.raw.columns.forEach(function (col) {
$scope.columns.push(col.aggParams ? col.aggParams.field : 'count');
formats.push(col.field ? col.field.format.convert : _.identity);
});
$scope.rows = $scope.chartData.raw.rows;
// sort the row values
if ($scope.sort) $scope.rows = orderBy($scope.rows, $scope.sort.getter, $scope.sort.asc);
// format all row values
$scope.rows = $scope.rows.map(function (row) {
return row.map(function (cell, i) {
return formats[i](cell);
});
});
});
});
}
};
};
});

View file

@ -1,69 +0,0 @@
define(function (require) {
require('modules')
.get('apps/visualize')
.directive('visualizeSpy', function (Private, $compile) {
var $ = require('jquery');
var _ = require('lodash');
var modes = _.flatten([
Private(require('apps/visualize/spy/_table')),
Private(require('apps/visualize/spy/_req_resp_stats'))
]);
var defaultMode = modes[0];
modes.byName = _.indexBy(modes, 'name');
return {
restrict: 'E',
template: require('text!apps/visualize/spy/_spy.html'),
link: function ($scope, $el) {
$scope.spyMode = null;
$scope.modes = modes;
$scope.toggleDisplay = function () {
$scope.setSpyMode($scope.spyMode ? null : defaultMode);
};
$scope.setSpyMode = function (newMode) {
// allow passing in a mode name
if (_.isString(newMode)) newMode = modes.byName[newMode];
var current = $scope.spyMode;
var change = false;
function set() {
// no change
if (current && newMode && newMode.name === current.name) return;
// clear the current value
if (current) {
current.$container.remove();
current.$scope.$destroy();
delete $scope.spyMode;
current = null;
change = true;
}
// no further changes
if (!newMode) return;
change = true;
current = $scope.spyMode = {
// copy a couple values over
name: newMode.name,
display: newMode.display,
$scope: $scope.$new(),
$container: $('<div class="visualize-spy-container">').appendTo($el)
};
current.$container.append($compile(newMode.template)(current.$scope));
newMode.link(current.$scope, current.$container);
}
// wrapped in fn to enable early return
set();
if (change) $scope.$emit('change:spyMode', newMode);
};
}
};
});
});

View file

@ -1,103 +0,0 @@
@import (reference) "../../../styles/_bootstrap.less";
@import (reference) "../../../styles/theme/_theme.less";
@import (reference) "../../../styles/_variables.less";
@import (reference) "lesshat.less";
@media (min-width: @screen-md-min) {
.vis-editor-content {
display: flex;
.flex-direction(row);
.justify-content(flex-start);
.vis-sidebar {
.flex(0, 0, 300px);
}
.vis-canvas {
.flex(1, 1, 100%);
}
}
}
.vis-editor-content {
vis-config-editor {
display: block;
}
.sidebar-item-title:hover {
color: inherit !important;
background-color: inherit !important;
}
.vis-config-details {
border-top: 1px solid @well-border;
padding: 5px 10px;
background-color: @body-bg;
color: @text-color;
.config-controls {
margin-bottom: 5px;
}
}
.agg-config-interval {
td {
padding-left: 10px;
&:first-child {
padding-left: 0px;
}
}
}
}
.vis-wizard {
h1 {
margin-top: 45px;
}
}
vis-canvas {
display: block;
}
.vis-editor navbar {
.bitty-modal-container {
position: relative;
.bitty-modal {
position: absolute;
cursor: pointer;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 10;
background: rgba(70, 82, 93, 0.9);
color: white;
text-align: center;
padding-top: 6px;
.user-select(none);
}
}
}
form.vis-share {
div.form-control {
height: inherit;
}
}
// vis-search-editor {
// display: block;
// background: @sidebar-bg;
// text-align: center;
// min-height: 0;
// border-bottom: 1px solid darken(@sidebar-bg, 10%);
// .user-select(none);
// color: @sidebar-color;
// a {
// color: @sidebar-color;
// }
// }

View file

@ -1,130 +0,0 @@
@import (reference) "../../../styles/_bootstrap.less";
@import (reference) "../../../styles/theme/_theme.less";
@import (reference) "../../../styles/_variables.less";
@import (reference) "lesshat.less";
visualize {
.display(flex);
.flex-direction(column);
height: 100%;
width: 100%;
overflow: auto;
position: relative;
.visualize-error {
margin-top: 20px;
text-align: center;
font-size: 1em;
.fa-exclamation-triangle {
font-size: 2em;
color: @btn-danger-bg;
}
}
.k4tip {
white-space: pre-line;
}
.visualize-chart {
.flex(1, 1);
overflow: hidden;
&.spy-visible {
margin-bottom: 10px;
}
&.spy-only {
display: none;
}
}
}
visualize-spy {
// this element should flex
.flex(0, 0, auto);
// it's children should also flex vertically
.flex-direction(column);
.display(flex);
overflow: auto;
padding-top: 10px;
&.visible {
display: block;
}
&.only {
.flex(1, 1, auto);
padding-top: 0px;
}
.visualize-show-spy {
.flex(0, 0, auto);
background-color: @well-bg;
text-align: center;
i {
padding: 0 10px;
}
}
.visualize-spy-container {
.flex(1, 1, auto);
.display(flex);
.flex-direction(column);
padding: 10px 10px 0;
overflow-y: auto;
tr > td {
font-size: 0.85em;
}
}
// this is the default, double-arrow sort that is just a hint to the user that they can sort
.visualize-table th i.fa-sort {
color: @gray-light;
}
.visualize-table-right {
text-align: right;
}
.visualize-table-controls {
padding: 0 5px;
text-align: right;
}
.visualize-csv-options {
.form-control {
padding: 0px 6px;
height: auto;
}
label {
margin: 0 10px;
padding: 0;
font-weight: normal;
}
.visualize-csv-separator {
width: 1.5em;
}
button[type=submit] {
margin-left: 5px;
}
}
.visualize-spy-tabs {
margin-top: 10px;
margin-bottom: 10px;
.flex(0, 0, auto);
}
pre {
word-break: break-all;
word-wrap: break-word;
white-space: pre-wrap;
}
}