mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactored panels to used dashboard, query and filter services and store them on the dashboard
This commit is contained in:
parent
0d560c24e6
commit
0d0d3f4a36
@ -123,8 +123,6 @@
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.popover-title { display: none; }
|
||||
|
||||
.tiny {
|
||||
font-size: 50%;
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('kibana.controllers', [])
|
||||
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus, fields, dashboard) {
|
||||
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus,
|
||||
fields, dashboard) {
|
||||
|
||||
var _d = {
|
||||
title: "",
|
||||
|
227
js/services.js
227
js/services.js
@ -84,6 +84,7 @@ angular.module('kibana.services', [])
|
||||
|
||||
})
|
||||
.service('kbnIndex',function($http) {
|
||||
|
||||
// returns a promise containing an array of all indices matching the index
|
||||
// pattern that exist in a given range
|
||||
this.indices = function(from,to,pattern,interval) {
|
||||
@ -233,14 +234,14 @@ angular.module('kibana.services', [])
|
||||
var _query = {
|
||||
query: '*',
|
||||
alias: '',
|
||||
color: colorAt(_id)
|
||||
color: colorAt(_id),
|
||||
id: _id
|
||||
}
|
||||
_.defaults(query,_query)
|
||||
self.list[_id] = query;
|
||||
self.ids.push(_id)
|
||||
return id;
|
||||
return _id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.remove = function(id) {
|
||||
@ -256,6 +257,10 @@ angular.module('kibana.services', [])
|
||||
}
|
||||
}
|
||||
|
||||
this.findQuery = function(queryString) {
|
||||
return _.findWhere(self.list,{query:queryString})
|
||||
}
|
||||
|
||||
var nextId = function() {
|
||||
if(_q.idQueue.length > 0) {
|
||||
return _q.idQueue.shift()
|
||||
@ -271,14 +276,185 @@ angular.module('kibana.services', [])
|
||||
init();
|
||||
|
||||
})
|
||||
.service('dashboard', function($routeParams, $http, $rootScope, ejsResource, timer) {
|
||||
.service('filterSrv', function(dashboard, ejsResource) {
|
||||
// Create an object to hold our service state on the dashboard
|
||||
dashboard.current.services.filter = dashboard.current.services.filter || {};
|
||||
_.defaults(dashboard.current.services.filter,{
|
||||
idQueue : [],
|
||||
list : {},
|
||||
ids : [],
|
||||
});
|
||||
|
||||
// For convenience
|
||||
var ejs = ejsResource(config.elasticsearch);
|
||||
var _f = dashboard.current.services.filter;
|
||||
|
||||
// Save a reference to this
|
||||
var self = this;
|
||||
|
||||
// Accessors
|
||||
this.list = dashboard.current.services.filter.list;
|
||||
this.ids = dashboard.current.services.filter.ids;
|
||||
|
||||
// This is used both for adding filters and modifying them.
|
||||
// If an id is passed, the filter at that id is updated
|
||||
this.set = function(filter,id) {
|
||||
_.defaults(filter,{mandate:'must'})
|
||||
if(!_.isUndefined(id)) {
|
||||
if(!_.isUndefined(self.list[id])) {
|
||||
_.extend(self.list[id],filter);
|
||||
return id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(_.isUndefined(filter.type)) {
|
||||
return false;
|
||||
} else {
|
||||
var _id = nextId();
|
||||
var _filter = {
|
||||
alias: '',
|
||||
id: _id
|
||||
}
|
||||
_.defaults(filter,_filter)
|
||||
self.list[_id] = filter;
|
||||
self.ids.push(_id)
|
||||
return _id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.getBoolFilter = function(ids) {
|
||||
// A default match all filter, just in case there are no other filters
|
||||
var bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
|
||||
_.each(ids,function(id) {
|
||||
switch(self.list[id].mandate)
|
||||
{
|
||||
case 'mustNot':
|
||||
bool = bool.mustNot(self.getEjsObj(id));
|
||||
break;
|
||||
case 'should':
|
||||
bool = bool.should(self.getEjsObj(id));
|
||||
break;
|
||||
default:
|
||||
bool = bool.must(self.getEjsObj(id));
|
||||
}
|
||||
})
|
||||
return bool;
|
||||
}
|
||||
|
||||
this.getEjsObj = function(id) {
|
||||
return self.toEjsObj(self.list[id])
|
||||
}
|
||||
|
||||
this.toEjsObj = function (filter) {
|
||||
switch(filter.type)
|
||||
{
|
||||
case 'time':
|
||||
return ejs.RangeFilter(filter.field)
|
||||
.from(filter.from)
|
||||
.to(filter.to)
|
||||
break;
|
||||
case 'range':
|
||||
return ejs.RangeFilter(filter.field)
|
||||
.from(filter.from)
|
||||
.to(filter.to)
|
||||
break;
|
||||
case 'querystring':
|
||||
console.log(filter.query)
|
||||
return ejs.QueryFilter(ejs.QueryStringQuery(filter.query))
|
||||
break;
|
||||
case 'terms':
|
||||
return ejs.TermsFilter(filter.field,filter.value)
|
||||
break;
|
||||
case 'exists':
|
||||
return ejs.ExistsFilter(filter.field)
|
||||
break;
|
||||
case 'missing':
|
||||
return ejs.MissingFilter(filter.field)
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.getByType = function(type) {
|
||||
return _.pick(self.list,self.idsByType(type))
|
||||
}
|
||||
|
||||
this.removeByType = function(type) {
|
||||
var ids = self.idsByType(type)
|
||||
_.each(ids,function(id) {
|
||||
self.remove(id)
|
||||
})
|
||||
return ids;
|
||||
}
|
||||
|
||||
this.idsByType = function(type) {
|
||||
return _.pluck(_.where(self.list,{type:type}),'id')
|
||||
}
|
||||
|
||||
// This special function looks for all time filters, and returns a time range according to the mode
|
||||
this.timeRange = function(mode) {
|
||||
var _t = _.where(self.list,{type:'time'})
|
||||
if(_t.length == 0) {
|
||||
return false;
|
||||
}
|
||||
switch(mode) {
|
||||
case "min":
|
||||
return {
|
||||
from: new Date(_.max(_.pluck(_t,'from'))),
|
||||
to: new Date(_.min(_.pluck(_t,'to')))
|
||||
}
|
||||
break;
|
||||
case "max":
|
||||
return {
|
||||
from: new Date(_.min(_.pluck(_t,'from'))),
|
||||
to: new Date(_.max(_.pluck(_t,'to')))
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.remove = function(id) {
|
||||
if(!_.isUndefined(self.list[id])) {
|
||||
delete self.list[id];
|
||||
// This must happen on the full path also since _.without returns a copy
|
||||
self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id)
|
||||
_f.idQueue.unshift(id)
|
||||
_f.idQueue.sort(function(a,b){return a-b});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var nextId = function() {
|
||||
if(_f.idQueue.length > 0) {
|
||||
return _f.idQueue.shift()
|
||||
} else {
|
||||
return self.ids.length;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.service('dashboard', function($routeParams, $http, $rootScope, $injector, ejsResource, timer, kbnIndex) {
|
||||
// A hash of defaults to use when loading a dashboard
|
||||
|
||||
var _dash = {
|
||||
title: "",
|
||||
editable: true,
|
||||
rows: [],
|
||||
services: {}
|
||||
services: {},
|
||||
index: {
|
||||
interval: 'none',
|
||||
pattern: '_all',
|
||||
default: '_all'
|
||||
},
|
||||
};
|
||||
|
||||
// An elasticJS client to use
|
||||
@ -288,9 +464,11 @@ angular.module('kibana.services', [])
|
||||
// Empty dashboard object
|
||||
this.current = {};
|
||||
this.last = {};
|
||||
this.indices = [];
|
||||
|
||||
// Store a reference to this
|
||||
var self = this;
|
||||
var filterSrv,query;
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess',function(){
|
||||
route();
|
||||
@ -326,6 +504,33 @@ angular.module('kibana.services', [])
|
||||
}
|
||||
}
|
||||
|
||||
// Since the dashboard is responsible for index computation, we can compute and assign the indices
|
||||
// here before telling the panels to refresh
|
||||
this.refresh = function() {
|
||||
if(self.current.index.interval !== 'none') {
|
||||
if(filterSrv.idsByType('time').length > 0) {
|
||||
var _range = filterSrv.timeRange('min');
|
||||
kbnIndex.indices(_range.from,_range.to,
|
||||
self.current.index.pattern,self.current.index.interval
|
||||
).then(function (p) {
|
||||
if(p.length > 0) {
|
||||
self.indices = p;
|
||||
} else {
|
||||
self.indices = [self.current.index.default]
|
||||
}
|
||||
$rootScope.$broadcast('refresh')
|
||||
});
|
||||
} else {
|
||||
// This is not optimal, we should be getting the entire index list here, or at least every
|
||||
// index that possibly matches the pattern
|
||||
self.indices = [self.current.index.default]
|
||||
}
|
||||
} else {
|
||||
self.indices = [self.current.index.pattern]
|
||||
$rootScope.$broadcast('refresh')
|
||||
}
|
||||
}
|
||||
|
||||
this.to_file = function() {
|
||||
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
|
||||
// from filesaver.js
|
||||
@ -422,8 +627,6 @@ angular.module('kibana.services', [])
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.elasticsearch_delete = function(id) {
|
||||
@ -497,8 +700,18 @@ angular.module('kibana.services', [])
|
||||
}
|
||||
|
||||
this.dash_load = function(dashboard) {
|
||||
|
||||
if(dashboard.index.interval === 'none') {
|
||||
self.indices = [dashboard.index.pattern]
|
||||
}
|
||||
|
||||
self.current = dashboard;
|
||||
|
||||
timer.cancel_all();
|
||||
|
||||
// Ok, now that we've setup the current dashboard, we can inject our services
|
||||
query = $injector.get('query');
|
||||
filterSrv = $injector.get('filterSrv')
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -12,17 +12,10 @@
|
||||
* field :: field containing a 2 element array in the format [lon,lat]
|
||||
* tooltip :: field to extract the tool tip value from
|
||||
* spyable :: Show the 'eye' icon that reveals the last ES query
|
||||
|
||||
### Group Events
|
||||
#### Sends
|
||||
* get_time :: On panel initialization get time range to query
|
||||
#### Receives
|
||||
* time :: An object containing the time range to use and the index(es) to query
|
||||
* query :: An Array of queries, this panel uses only the first one
|
||||
*/
|
||||
|
||||
angular.module('kibana.bettermap', [])
|
||||
.controller('bettermap', function($scope, eventBus, query) {
|
||||
.controller('bettermap', function($scope, eventBus, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -37,50 +30,53 @@ angular.module('kibana.bettermap', [])
|
||||
_.defaults($scope.panel,_d)
|
||||
|
||||
$scope.init = function() {
|
||||
|
||||
$scope.$on('refresh',function(){
|
||||
$scope.get_data();
|
||||
})
|
||||
|
||||
eventBus.register($scope,'time', function(event,time){set_time(time)});
|
||||
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.get_data = function(segment,query_id) {
|
||||
$scope.get_data = function(segment,query_id) {
|
||||
$scope.panel.error = false;
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
return
|
||||
if(dashboard.indices.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_.isUndefined($scope.panel.field)) {
|
||||
$scope.panel.error = "Please select a field that contains geo point in [lon,lat] format"
|
||||
return
|
||||
}
|
||||
|
||||
//$scope.panel.loading = true;
|
||||
// Determine the field to sort on
|
||||
var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'))
|
||||
if(timeField.length > 1) {
|
||||
$scope.panel.error = "Time field must be consistent amongst time filters"
|
||||
} else if(timeField.length == 0) {
|
||||
timeField = null;
|
||||
} else {
|
||||
timeField = timeField[0]
|
||||
}
|
||||
|
||||
var _segment = _.isUndefined(segment) ? 0 : segment
|
||||
$scope.segment = _segment;
|
||||
|
||||
var boolQuery = ejs.BoolQuery();
|
||||
_.each(query.list,function(q) {
|
||||
boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*') + " AND _exists_:"+$scope.panel.field))
|
||||
boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*')))
|
||||
})
|
||||
|
||||
var request = $scope.ejs.Request().indices($scope.index[_segment])
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
|
||||
.query(ejs.FilteredQuery(
|
||||
boolQuery,
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
)
|
||||
)
|
||||
filterSrv.getBoolFilter(filterSrv.ids).must(ejs.ExistsFilter($scope.panel.field))
|
||||
))
|
||||
.fields([$scope.panel.field,$scope.panel.tooltip])
|
||||
.size($scope.panel.size)
|
||||
.sort($scope.time.field,'desc');
|
||||
|
||||
if(!_.isNull(timeField)) {
|
||||
request = request.sort(timeField,'desc');
|
||||
}
|
||||
|
||||
$scope.populate_modal(request)
|
||||
|
||||
@ -125,7 +121,7 @@ $scope.get_data = function(segment,query_id) {
|
||||
$scope.$emit('draw')
|
||||
|
||||
// Get $size results then stop querying
|
||||
if($scope.data.length < $scope.panel.size && _segment+1 < $scope.index.length)
|
||||
if($scope.data.length < $scope.panel.size && _segment+1 < dashboard.indices.length)
|
||||
$scope.get_data(_segment+1,$scope.query_id)
|
||||
|
||||
});
|
||||
@ -136,18 +132,12 @@ $scope.get_data = function(segment,query_id) {
|
||||
$scope.modal = {
|
||||
title: "Inspector",
|
||||
body : "<h5>Last Elasticsearch Query</h5><pre>"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
|
||||
angular.toJson(JSON.parse(request.toString()),true)+
|
||||
"'</pre>",
|
||||
}
|
||||
}
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
})
|
||||
.directive('bettermap', function() {
|
||||
return {
|
||||
|
@ -1,22 +1,26 @@
|
||||
<div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
The derive queries panel takes a query and a field, then runs a terms facet against both and generates a list of terms to query on. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p>
|
||||
Query Mode allows to optionally append original query to each term in the list.
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span1">
|
||||
<label class="small">Length</label>
|
||||
<input type="number" style="width:80%" ng-model="panel.size" ng-change="set_refresh(true)">
|
||||
</div>
|
||||
<div class="span3">
|
||||
<label class="small">Field</label>
|
||||
<input type="text" bs-typeahead="fields.list" style="width:80%" ng-change="set_refresh(true)" ng-model='panel.field'></select>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<label class="small">Query Mode</label>
|
||||
<select style="width:80%" ng-change="set_refresh(true)" ng-model='panel.mode' ng-options="f for f in ['terms only','AND', 'OR']"></select>
|
||||
</div>
|
||||
<div class="span8">
|
||||
<div class="span5">
|
||||
<label class="small">Exclude Terms(s) (comma seperated)</label>
|
||||
<input array-join type="text" style="width:90%" ng-change="set_refresh(true)" ng-model='panel.exclude'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
The derive queries panel takes a query and a field, runs a terms facet, then creates queries based on them. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p>
|
||||
Query Mode allows to optionally append original query to each term in the list.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,23 @@
|
||||
<kibana-panel ng-controller='derivequeries' ng-init="init()">
|
||||
<style>
|
||||
.end-derive {
|
||||
position:absolute;
|
||||
right:15px;
|
||||
top:5px;
|
||||
}
|
||||
.panel-derive {
|
||||
padding-right: 35px !important;
|
||||
height: 31px !important;
|
||||
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
|
||||
-moz-box-sizing: border-box; /* Firefox, other Gecko */
|
||||
box-sizing: border-box; /* Opera/IE 8+ */
|
||||
}
|
||||
</style>
|
||||
<span ng-show='panel.spyable' style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
|
||||
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
|
||||
</span>
|
||||
<div ng-show="!panel.multi">
|
||||
<!--
|
||||
<div>
|
||||
<form>
|
||||
<table class="form-horizontal">
|
||||
<tr>
|
||||
@ -29,4 +44,16 @@
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
||||
<label class="small">Create new queries from <strong>{{panel.field}}</strong> ({{panel.mode}} mode)</label>
|
||||
<div>
|
||||
<form class="form-search" style="position:relative" ng-submit="get_data()">
|
||||
<input class="search-query panel-derive input-block-level" bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="panel.query"/>
|
||||
<span class="end-derive">
|
||||
<i class="icon-search pointer" ng-click="get_data()"></i>
|
||||
</span
|
||||
</form>
|
||||
</div>
|
||||
</kibana-panel>
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
## Termsquery
|
||||
## Derivequeries
|
||||
|
||||
Broadcasts an array of queries based on the results of a terms facet
|
||||
|
||||
@ -23,13 +23,15 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.derivequeries', [])
|
||||
.controller('derivequeries', function($scope, eventBus) {
|
||||
.controller('derivequeries', function($scope, $rootScope, query, eventBus, fields, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
loading : false,
|
||||
status : "Beta",
|
||||
label : "Search",
|
||||
query : "*",
|
||||
ids : [],
|
||||
group : "default",
|
||||
field : '_type',
|
||||
fields : [],
|
||||
@ -43,26 +45,19 @@ angular.module('kibana.derivequeries', [])
|
||||
_.defaults($scope.panel,_d);
|
||||
|
||||
$scope.init = function() {
|
||||
eventBus.register($scope,'fields', function(event, fields) {
|
||||
$scope.panel.fields = fields.all;
|
||||
});
|
||||
eventBus.register($scope,'time', function(event,time){set_time(time)});
|
||||
eventBus.register($scope,'query', function(event, query) {
|
||||
$scope.panel.query = _.isArray(query) ? query[0] : query;
|
||||
$scope.get_data();
|
||||
});
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
$scope.panel.fields = fields.list
|
||||
}
|
||||
|
||||
$scope.get_data = function() {
|
||||
update_history($scope.panel.query);
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
$scope.panel.loading = true;
|
||||
var request = $scope.ejs.Request().indices($scope.index);
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices);
|
||||
|
||||
// Terms mode
|
||||
request = request
|
||||
@ -73,9 +68,7 @@ angular.module('kibana.derivequeries', [])
|
||||
.facetFilter(ejs.QueryFilter(
|
||||
ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery($scope.panel.query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
filterSrv.getBoolFilter(filterSrv.ids)
|
||||
)))).size(0)
|
||||
|
||||
$scope.populate_modal(request);
|
||||
@ -93,10 +86,22 @@ angular.module('kibana.derivequeries', [])
|
||||
} else if ($scope.panel.mode === 'OR') {
|
||||
var suffix = ' OR (' + $scope.panel.query + ')';
|
||||
}
|
||||
var ids = [];
|
||||
_.each(results.facets.query.terms, function(v) {
|
||||
data.push($scope.panel.field+':"'+v.term+'"'+suffix)
|
||||
var _q = $scope.panel.field+':"'+v.term+'"'+suffix;
|
||||
// if it isn't in the list, remove it
|
||||
var _iq = query.findQuery(_q)
|
||||
if(!_iq) {
|
||||
ids.push(query.set({query:_q}));
|
||||
} else {
|
||||
ids.push(_iq.id);
|
||||
}
|
||||
});
|
||||
$scope.send_query(data)
|
||||
_.each(_.difference($scope.panel.ids,ids),function(id){
|
||||
query.remove(id)
|
||||
})
|
||||
$scope.panel.ids = ids;
|
||||
dashboard.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@ -114,23 +119,12 @@ angular.module('kibana.derivequeries', [])
|
||||
$scope.modal = {
|
||||
title: "Inspector",
|
||||
body : "<h5>Last Elasticsearch Query</h5><pre>"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
|
||||
angular.toJson(JSON.parse(request.toString()),true)+
|
||||
"'</pre>",
|
||||
}
|
||||
}
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.send_query = function(query) {
|
||||
var _query = _.isArray(query) ? query : [query]
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'query',_query)
|
||||
}
|
||||
|
||||
var update_history = function(query) {
|
||||
query = _.isArray(query) ? query : [query];
|
||||
if($scope.panel.remember > 0) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
<a class="close" ng-click="dismiss()" href="">×</a>
|
||||
<h4>
|
||||
Micro Analysis of {{micropanel.field}}
|
||||
<i class="pointer icon-search" ng-click="build_search('_exists_',micropanel.field);dismiss();"></i>
|
||||
<i class="pointer icon-ban-circle" ng-click="build_search('_missing_',micropanel.field);dismiss();"></i>
|
||||
<i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'exists');dismiss();"></i>
|
||||
<i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'missing');dismiss();"></i>
|
||||
<br><small>{{micropanel.count}} events in the table set</small>
|
||||
</h4>
|
||||
<table style="width:480px" class='table table-bordered table-striped table-condensed'>
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
*/
|
||||
angular.module('kibana.fields', [])
|
||||
.controller('fields', function($scope, eventBus, $timeout) {
|
||||
.controller('fields', function($scope, eventBus, $timeout, dashboard, query, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -79,9 +79,15 @@ angular.module('kibana.fields', [])
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,"selected_fields",$scope.active)
|
||||
}
|
||||
|
||||
$scope.build_search = function(field, value,negate) {
|
||||
$scope.panel.query = [add_to_query($scope.panel.query,field,value,negate)]
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query);
|
||||
$scope.build_search = function(field,value,negate) {
|
||||
var query = (negate ? '-':'+')+field+":\""+value+"\""
|
||||
filterSrv.set({type:'querystring',query:query})
|
||||
dashboard.refresh();
|
||||
}
|
||||
|
||||
$scope.fieldExists = function(field,mode) {
|
||||
filterSrv.set({type:mode,field:field})
|
||||
dashboard.refresh();
|
||||
}
|
||||
|
||||
$scope.is_active = function(field) {
|
||||
|
@ -4,13 +4,19 @@
|
||||
<label class="small">Mode</label>
|
||||
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
|
||||
</div>
|
||||
<div class="span3" ng-show="panel.mode != 'count'">
|
||||
<label class="small">Field</label>
|
||||
<div class="span2">
|
||||
<label class="small">Time Field</label>
|
||||
<form>
|
||||
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.time_field">
|
||||
</form>
|
||||
</div>
|
||||
<div class="span2" ng-show="panel.mode != 'count'">
|
||||
<label class="small">Value Field</label>
|
||||
<form>
|
||||
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.value_field">
|
||||
</form>
|
||||
</div>
|
||||
<div class="span5" ng-show="panel.mode != 'count'">
|
||||
<div class="span3" ng-show="panel.mode != 'count'">
|
||||
<label class="small">Note</label><small> In <strong>{{panel.mode}}</strong> mode the configured field <strong>must</strong> be a numeric type</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,12 +22,12 @@
|
||||
</span>
|
||||
<div>
|
||||
<span ng-show='panel.zoomlinks && data'>
|
||||
<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>
|
||||
<!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>-->
|
||||
<a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |
|
||||
</span>
|
||||
<span ng-show="panel.legend" ng-repeat='series in plot.getData()' class="histogram-legend">
|
||||
<div class="histogram-legend-dot" style="background:{{series.color}};"></div>
|
||||
<div class='small histogram-legend-item'>{{series.label}} ({{series.hits}})</div>
|
||||
<span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend">
|
||||
<div class="histogram-legend-dot" style="background:{{series.info.color}};"></div>
|
||||
<div class='small histogram-legend-item'>{{series.info.alias}} ({{series.hits}})</div>
|
||||
</span>
|
||||
<span ng-show="panel.legend" class="small"><span ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> hits)</span>
|
||||
</div>
|
||||
|
@ -42,7 +42,7 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.histogram', [])
|
||||
.controller('histogram', function($scope, eventBus,query) {
|
||||
.controller('histogram', function($scope, eventBus, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -50,6 +50,7 @@ angular.module('kibana.histogram', [])
|
||||
group : "default",
|
||||
query : [ {query: "*", label:"Query"} ],
|
||||
mode : 'count',
|
||||
time_field : '@timestamp',
|
||||
value_field : null,
|
||||
auto_int : true,
|
||||
resolution : 100,
|
||||
@ -75,50 +76,46 @@ angular.module('kibana.histogram', [])
|
||||
|
||||
$scope.queries = query;
|
||||
|
||||
eventBus.register($scope,'time', function(event,time){$scope.set_time(time)});
|
||||
|
||||
$scope.$on('refresh',function(){
|
||||
$scope.get_data();
|
||||
})
|
||||
|
||||
// Now that we're all setup, request the time from our group if we don't
|
||||
// have it yet
|
||||
if(_.isUndefined($scope.time))
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
}
|
||||
|
||||
$scope.get_data = function(segment,query_id) {
|
||||
delete $scope.panel.error
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
var _range = $scope.range = filterSrv.timeRange('min');
|
||||
|
||||
if ($scope.panel.auto_int)
|
||||
$scope.panel.interval = secondsToHms(calculate_interval($scope.time.from,$scope.time.to,$scope.panel.resolution,0)/1000);
|
||||
|
||||
$scope.panel.interval = secondsToHms(calculate_interval(_range.from,_range.to,$scope.panel.resolution,0)/1000);
|
||||
|
||||
$scope.panel.loading = true;
|
||||
var _segment = _.isUndefined(segment) ? 0 : segment
|
||||
var request = $scope.ejs.Request().indices($scope.index[_segment]);
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
|
||||
|
||||
// Build the query
|
||||
_.each($scope.queries.ids, function(id) {
|
||||
var query = $scope.ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery($scope.queries.list[id].query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
filterSrv.getBoolFilter(filterSrv.ids)
|
||||
)
|
||||
|
||||
var facet = $scope.ejs.DateHistogramFacet(id)
|
||||
|
||||
if($scope.panel.mode === 'count') {
|
||||
facet = facet.field($scope.time.field)
|
||||
facet = facet.field($scope.panel.time_field)
|
||||
} else {
|
||||
if(_.isNull($scope.panel.value_field)) {
|
||||
$scope.panel.error = "In " + $scope.panel.mode + " mode a field must be specified";
|
||||
return
|
||||
}
|
||||
facet = facet.keyField($scope.time.field).valueField($scope.panel.value_field)
|
||||
facet = facet.keyField($scope.panel.time_field).valueField($scope.panel.value_field)
|
||||
}
|
||||
facet = facet.interval($scope.panel.interval).facetFilter($scope.ejs.QueryFilter(query))
|
||||
request = request.facet(facet).size(0)
|
||||
@ -132,6 +129,7 @@ angular.module('kibana.histogram', [])
|
||||
|
||||
// Populate scope when we have results
|
||||
results.then(function(results) {
|
||||
|
||||
$scope.panel.loading = false;
|
||||
if(_segment == 0) {
|
||||
$scope.hits = 0;
|
||||
@ -145,15 +143,21 @@ angular.module('kibana.histogram', [])
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're still on the same query
|
||||
if($scope.query_id === query_id) {
|
||||
// Convert facet ids to numbers
|
||||
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);})
|
||||
|
||||
// Make sure we're still on the same query/queries
|
||||
if($scope.query_id === query_id &&
|
||||
_.intersection(facetIds,query.ids).length == query.ids.length
|
||||
) {
|
||||
|
||||
var i = 0;
|
||||
_.each(results.facets, function(v, id) {
|
||||
_.each(query.ids, function(id) {
|
||||
var v = results.facets[id];
|
||||
|
||||
// Null values at each end of the time range ensure we see entire range
|
||||
if(_.isUndefined($scope.data[i]) || _segment == 0) {
|
||||
var data = [[$scope.time.from.getTime(), null],[$scope.time.to.getTime(), null]];
|
||||
var data = [[_range.from.getTime(), null],[_range.to.getTime(), null]];
|
||||
var hits = 0;
|
||||
} else {
|
||||
var data = $scope.data[i].data
|
||||
@ -172,7 +176,7 @@ angular.module('kibana.histogram', [])
|
||||
// Create the flot series object
|
||||
var series = {
|
||||
data: {
|
||||
id: id,
|
||||
info: $scope.queries.list[id],
|
||||
data: data,
|
||||
hits: hits
|
||||
},
|
||||
@ -187,7 +191,7 @@ angular.module('kibana.histogram', [])
|
||||
$scope.$emit('render')
|
||||
|
||||
// If we still have segments left, get them
|
||||
if(_segment < $scope.index.length-1) {
|
||||
if(_segment < dashboard.indices.length-1) {
|
||||
$scope.get_data(_segment+1,query_id)
|
||||
}
|
||||
|
||||
@ -198,7 +202,32 @@ angular.module('kibana.histogram', [])
|
||||
// function $scope.zoom
|
||||
// factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
|
||||
$scope.zoom = function(factor) {
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'zoom',factor);
|
||||
var _now = Date.now();
|
||||
var _range = filterSrv.timeRange('min');
|
||||
var _timespan = (_range.to.valueOf() - _range.from.valueOf());
|
||||
var _center = _range.to.valueOf() - _timespan/2
|
||||
|
||||
var _to = (_center + (_timespan*factor)/2)
|
||||
var _from = (_center - (_timespan*factor)/2)
|
||||
|
||||
// If we're not already looking into the future, don't.
|
||||
if(_to > Date.now() && _range.to < Date.now()) {
|
||||
var _offset = _to - Date.now()
|
||||
_from = _from - _offset
|
||||
_to = Date.now();
|
||||
}
|
||||
|
||||
if(factor > 1) {
|
||||
filterSrv.removeByType('time')
|
||||
}
|
||||
filterSrv.set({
|
||||
type:'time',
|
||||
from:moment.utc(_from),
|
||||
to:moment.utc(_to),
|
||||
field:$scope.panel.time_field
|
||||
})
|
||||
dashboard.refresh();
|
||||
|
||||
}
|
||||
|
||||
// I really don't like this function, too much dom manip. Break out into directive?
|
||||
@ -223,14 +252,8 @@ angular.module('kibana.histogram', [])
|
||||
$scope.$emit('render');
|
||||
}
|
||||
|
||||
$scope.set_time = function(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = time.index || $scope.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
})
|
||||
.directive('histogramChart', function(eventBus) {
|
||||
.directive('histogramChart', function(dashboard, eventBus, filterSrv, $rootScope) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
@ -249,10 +272,12 @@ angular.module('kibana.histogram', [])
|
||||
function render_panel() {
|
||||
|
||||
// Populate from the query service
|
||||
_.each(scope.data,function(series) {
|
||||
series.label = scope.queries.list[series.id].alias,
|
||||
series.color = scope.queries.list[series.id].color
|
||||
})
|
||||
try {
|
||||
_.each(scope.data,function(series) {
|
||||
series.label = series.info.alias,
|
||||
series.color = series.info.color
|
||||
})
|
||||
} catch(e) {return}
|
||||
|
||||
// Set barwidth based on specified interval
|
||||
var barwidth = interval_to_seconds(scope.panel.interval)*1000
|
||||
@ -366,9 +391,13 @@ angular.module('kibana.histogram', [])
|
||||
});
|
||||
|
||||
elem.bind("plotselected", function (event, ranges) {
|
||||
scope.time.from = moment(ranges.xaxis.from);
|
||||
scope.time.to = moment(ranges.xaxis.to)
|
||||
eventBus.broadcast(scope.$id,scope.panel.group,'set_time',scope.time)
|
||||
var _id = filterSrv.set({
|
||||
type : 'time',
|
||||
from : moment.utc(ranges.xaxis.from),
|
||||
to : moment.utc(ranges.xaxis.to),
|
||||
field : scope.panel.time_field
|
||||
})
|
||||
dashboard.refresh();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -3,14 +3,14 @@
|
||||
<div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
|
||||
<!-- vertical legend -->
|
||||
<table class="small" ng-show="panel.arrangement == 'vertical'">
|
||||
<tr ng-repeat="query in plot.getData()">
|
||||
<td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
|
||||
<tr ng-repeat="query in data">
|
||||
<td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- horizontal legend -->
|
||||
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
|
||||
<span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
|
||||
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;">
|
||||
<span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span>
|
||||
</div><br>
|
||||
|
||||
</div>
|
||||
@ -20,23 +20,26 @@
|
||||
<div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
|
||||
|
||||
<div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
|
||||
|
||||
<!-- vertical legend -->
|
||||
<table class="small" ng-show="panel.arrangement == 'vertical'">
|
||||
<tr ng-repeat="query in plot.getData()">
|
||||
<td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
|
||||
<tr ng-repeat="query in data">
|
||||
<td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- horizontal legend -->
|
||||
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
|
||||
<span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
|
||||
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;">
|
||||
<span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span>
|
||||
</div><br>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-show="panel.chart == 'total'"><div ng-style="panel.style" style="line-height:{{panel.style['font-size']}}">{{hits}}</div></div>
|
||||
|
||||
<span ng-show="panel.chart == 'list'"><span ng-style="panel.style" style="line-height:{{panel.style['font-size']}}" ng-repeat="query in data">{{query.label}} ({{query.hits}})<span></span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'">
|
||||
<span ng-show="panel.chart == 'list'">
|
||||
<div ng-style="panel.style" style="display:inline-block;line-height:{{panel.style['font-size']}}" ng-repeat="query in data">
|
||||
<i class="icon-circle" style="color:{{query.info.color}}"></i> {{query.info.alias}} ({{query.hits}})
|
||||
</div>
|
||||
</span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'">
|
||||
|
||||
</kibana-panel>
|
@ -22,7 +22,7 @@
|
||||
|
||||
*/
|
||||
angular.module('kibana.hits', [])
|
||||
.controller('hits', function($scope, eventBus, query) {
|
||||
.controller('hits', function($scope, eventBus, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -30,8 +30,8 @@ angular.module('kibana.hits', [])
|
||||
query : ["*"],
|
||||
group : "default",
|
||||
style : { "font-size": '10pt'},
|
||||
arrangement : 'vertical',
|
||||
chart : 'none',
|
||||
arrangement : 'horizontal',
|
||||
chart : 'bar',
|
||||
counter_pos : 'above',
|
||||
donut : false,
|
||||
tilt : false,
|
||||
@ -40,22 +40,13 @@ angular.module('kibana.hits', [])
|
||||
_.defaults($scope.panel,_d)
|
||||
|
||||
$scope.init = function () {
|
||||
$scope.queries = query;
|
||||
|
||||
$scope.hits = 0;
|
||||
eventBus.register($scope,'time', function(event,time){
|
||||
set_time(time)
|
||||
});
|
||||
|
||||
|
||||
|
||||
$scope.$on('refresh',function(){
|
||||
console.log($scope.queries)
|
||||
console.log(query)
|
||||
$scope.get_data();
|
||||
})
|
||||
$scope.get_data();
|
||||
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
}
|
||||
|
||||
$scope.get_data = function(segment,query_id) {
|
||||
@ -63,23 +54,22 @@ angular.module('kibana.hits', [])
|
||||
$scope.panel.loading = true;
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
var _segment = _.isUndefined(segment) ? 0 : segment
|
||||
var request = $scope.ejs.Request().indices($scope.index[_segment]);
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
|
||||
|
||||
// Build the question part of the query
|
||||
_.each($scope.queries.ids, function(id) {
|
||||
var query = $scope.ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery($scope.queries.list[id].query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to))
|
||||
_.each(query.ids, function(id) {
|
||||
var _q = $scope.ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery(query.list[id].query || '*'),
|
||||
filterSrv.getBoolFilter(filterSrv.ids));
|
||||
|
||||
request = request
|
||||
.facet($scope.ejs.QueryFacet(id)
|
||||
.query(query)
|
||||
.query(_q)
|
||||
).size(0)
|
||||
});
|
||||
|
||||
@ -91,7 +81,6 @@ angular.module('kibana.hits', [])
|
||||
|
||||
// Populate scope when we have results
|
||||
results.then(function(results) {
|
||||
|
||||
$scope.panel.loading = false;
|
||||
if(_segment == 0) {
|
||||
$scope.hits = 0;
|
||||
@ -104,16 +93,24 @@ angular.module('kibana.hits', [])
|
||||
$scope.panel.error = $scope.parse_error(results.error);
|
||||
return;
|
||||
}
|
||||
if($scope.query_id === query_id) {
|
||||
|
||||
// Convert facet ids to numbers
|
||||
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);})
|
||||
|
||||
// Make sure we're still on the same query/queries
|
||||
if($scope.query_id === query_id &&
|
||||
_.intersection(facetIds,query.ids).length == query.ids.length
|
||||
) {
|
||||
var i = 0;
|
||||
_.each(results.facets, function(v, id) {
|
||||
_.each(query.ids, function(id) {
|
||||
var v = results.facets[id]
|
||||
var hits = _.isUndefined($scope.data[i]) || _segment == 0 ?
|
||||
v.count : $scope.data[i].hits+v.count
|
||||
$scope.hits += v.count
|
||||
|
||||
// Create series
|
||||
$scope.data[i] = {
|
||||
//label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
|
||||
info: query.list[id],
|
||||
id: id,
|
||||
hits: hits,
|
||||
data: [[i,hits]]
|
||||
@ -122,26 +119,13 @@ angular.module('kibana.hits', [])
|
||||
i++;
|
||||
});
|
||||
$scope.$emit('render');
|
||||
if(_segment < $scope.index.length-1)
|
||||
if(_segment < dashboard.indices.length-1)
|
||||
$scope.get_data(_segment+1,query_id)
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.remove_query = function(q) {
|
||||
$scope.panel.query = _.without($scope.panel.query,q);
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.add_query = function(label,query) {
|
||||
$scope.panel.query.unshift({
|
||||
query: query,
|
||||
label: label,
|
||||
});
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.set_refresh = function (state) {
|
||||
$scope.refresh = state;
|
||||
}
|
||||
@ -155,11 +139,10 @@ angular.module('kibana.hits', [])
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
}).directive('hitsChart', function(eventBus) {
|
||||
}).directive('hitsChart', function(eventBus, query) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
@ -177,10 +160,12 @@ angular.module('kibana.hits', [])
|
||||
// Function for rendering panel
|
||||
function render_panel() {
|
||||
|
||||
_.each(scope.data,function(series) {
|
||||
series.label = scope.queries.list[series.id].alias,
|
||||
series.color = scope.queries.list[series.id].color
|
||||
})
|
||||
try {
|
||||
_.each(scope.data,function(series) {
|
||||
series.label = series.info.alias,
|
||||
series.color = series.info.color
|
||||
})
|
||||
} catch(e) {return}
|
||||
|
||||
var scripts = $LAB.script("common/lib/panels/jquery.flot.js").wait()
|
||||
.script("common/lib/panels/jquery.flot.pie.js")
|
||||
@ -201,13 +186,12 @@ angular.module('kibana.hits', [])
|
||||
yaxis: { show: true, min: 0, color: "#c8c8c8" },
|
||||
xaxis: { show: false },
|
||||
grid: {
|
||||
backgroundColor: '#272b30',
|
||||
borderWidth: 0,
|
||||
borderColor: '#eee',
|
||||
color: "#eee",
|
||||
hoverable: true,
|
||||
},
|
||||
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
|
||||
colors: query.colors
|
||||
})
|
||||
if(scope.panel.chart === 'pie')
|
||||
scope.plot = $.plot(elem, scope.data, {
|
||||
@ -223,7 +207,6 @@ angular.module('kibana.hits', [])
|
||||
label: 'The Rest'
|
||||
},
|
||||
stroke: {
|
||||
color: '#272b30',
|
||||
width: 0
|
||||
},
|
||||
label: {
|
||||
@ -239,7 +222,7 @@ angular.module('kibana.hits', [])
|
||||
},
|
||||
//grid: { hoverable: true, clickable: true },
|
||||
grid: { hoverable: true, clickable: true },
|
||||
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
|
||||
colors: query.colors
|
||||
});
|
||||
|
||||
// Compensate for the height of the legend. Gross
|
||||
|
@ -3,6 +3,7 @@
|
||||
.jvectormap-label {
|
||||
position: absolute;
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
border: solid 1px #CDCDCD;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
@ -30,6 +31,10 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jvectormap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.jvectormap-zoomin {
|
||||
display: none;
|
||||
top: 10px;
|
||||
@ -39,9 +44,23 @@
|
||||
display: none;
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
.map-legend {
|
||||
color : #c8c8c8;
|
||||
padding : 10px;
|
||||
font-size: 11pt;
|
||||
font-weight: 200;
|
||||
background-color: #1f1f1f;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 15px;
|
||||
display: none;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
<span ng-show="panel.spyable" class='spy panelextra pointer'>
|
||||
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
|
||||
</span>
|
||||
<div map params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
|
||||
<div class="jvectormap" map params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
|
||||
</kibana-panel>
|
@ -28,7 +28,7 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.map', [])
|
||||
.controller('map', function($scope, eventBus) {
|
||||
.controller('map', function($scope, $rootScope, eventBus, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -45,22 +45,24 @@ angular.module('kibana.map', [])
|
||||
_.defaults($scope.panel,_d)
|
||||
|
||||
$scope.init = function() {
|
||||
eventBus.register($scope,'time', function(event,time){set_time(time)});
|
||||
eventBus.register($scope,'query', function(event, query) {
|
||||
$scope.panel.query = _.isArray(query) ? query[0] : query;
|
||||
$scope.get_data();
|
||||
});
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
$scope.$on('refresh',function(){$scope.get_data()})
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.get_data = function() {
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
$scope.panel.loading = true;
|
||||
var request = $scope.ejs.Request().indices($scope.index);
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices);
|
||||
|
||||
var boolQuery = ejs.BoolQuery();
|
||||
_.each(query.list,function(q) {
|
||||
boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*'))
|
||||
})
|
||||
|
||||
// Then the insert into facet and make the request
|
||||
var request = request
|
||||
@ -70,10 +72,8 @@ angular.module('kibana.map', [])
|
||||
.exclude($scope.panel.exclude)
|
||||
.facetFilter(ejs.QueryFilter(
|
||||
ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery($scope.panel.query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
boolQuery,
|
||||
filterSrv.getBoolFilter(filterSrv.ids)
|
||||
)))).size(0);
|
||||
|
||||
$scope.populate_modal(request);
|
||||
@ -97,22 +97,17 @@ angular.module('kibana.map', [])
|
||||
$scope.modal = {
|
||||
title: "Inspector",
|
||||
body : "<h5>Last Elasticsearch Query</h5><pre>"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
|
||||
angular.toJson(JSON.parse(request.toString()),true)+
|
||||
"'</pre>",
|
||||
}
|
||||
}
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.build_search = function(field,value) {
|
||||
$scope.panel.query = add_to_query($scope.panel.query,field,value,false)
|
||||
$scope.get_data();
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
|
||||
_.each(query.list,function(q) {
|
||||
q.query = add_to_query(q.query,field,value,false);
|
||||
})
|
||||
dashboard.refresh();
|
||||
}
|
||||
|
||||
})
|
||||
@ -155,20 +150,12 @@ angular.module('kibana.map', [])
|
||||
}]
|
||||
},
|
||||
onRegionLabelShow: function(event, label, code){
|
||||
$('.jvectormap-label').css({
|
||||
"position" : "absolute",
|
||||
"display" : "none",
|
||||
'color' : "#c8c8c8",
|
||||
'padding' : '10px',
|
||||
'font-size' : '11pt',
|
||||
'font-weight' : 200,
|
||||
'background-color': '#1f1f1f',
|
||||
'border-radius': '5px'
|
||||
})
|
||||
elem.children('.map-legend').show()
|
||||
var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
|
||||
$('.jvectormap-label').text(label.text() + ": " + count);
|
||||
elem.children('.map-legend').text(label.text() + ": " + count);
|
||||
},
|
||||
onRegionOut: function(event, code) {
|
||||
$('.map-legend').hide();
|
||||
},
|
||||
onRegionClick: function(event, code) {
|
||||
var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
|
||||
@ -176,6 +163,8 @@ angular.module('kibana.map', [])
|
||||
scope.build_search(scope.panel.field,code)
|
||||
}
|
||||
});
|
||||
elem.prepend('<span class="map-legend"></span>');
|
||||
$('.map-legend').hide();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +1,62 @@
|
||||
<div class="row-fluid" ng-switch="panel.mode">
|
||||
<div class="span3">
|
||||
<label class="small">Mode</label>
|
||||
<select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
|
||||
</div>
|
||||
<div ng-switch-when="terms">
|
||||
<div>
|
||||
<div class="row-fluid" ng-switch="panel.mode">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<form style="margin-bottom: 0px">
|
||||
<label class="small">Field</label>
|
||||
<input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field">
|
||||
</form>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<form class="input-append" style="margin-bottom: 0px">
|
||||
<label class="small">Query</label>
|
||||
<input type="text" style="width:80%" ng-model="panel.query.query">
|
||||
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<label class="small">Length</label>
|
||||
<input type="number" style="width:80%" ng-model="panel.size" ng-change="get_data()">
|
||||
</div>
|
||||
<div class="span8">
|
||||
<form class="input-append" style="margin-bottom: 0px">
|
||||
<label class="small">Exclude Terms(s) (comma seperated)</label>
|
||||
<input array-join type="text" style="width:90%" ng-model='panel.exclude'></input>
|
||||
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="goal">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<label class="small">Mode</label>
|
||||
<select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
|
||||
</div>
|
||||
<div class="span2">
|
||||
<form style="margin-bottom: 0px">
|
||||
<label class="small">Goal</label>
|
||||
<input type="number" style="width:90%" ng-model="panel.query.goal">
|
||||
</form>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<form class="input-append" style="margin-bottom: 0px">
|
||||
<label class="small">Query</label>
|
||||
<input type="text" style="width:80%" ng-model="panel.query.query">
|
||||
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
|
||||
</form>
|
||||
<label class="small">Mode</label>
|
||||
<select class="input-small" ng-change="set_mode(panel.mode);set_refresh(true)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="terms">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<label class="small">Field</label>
|
||||
<input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.query.field" ng-change="set_refresh(true)">
|
||||
</div>
|
||||
<div class="span2">
|
||||
<label class="small">Length</label>
|
||||
<input class="input-small" type="number" ng-model="panel.size" ng-change="set_refresh(true)">
|
||||
</div>
|
||||
<div class="span6">
|
||||
<label class="small">Exclude Terms(s) (comma seperated)</label>
|
||||
<input array-join type="text" ng-model='panel.exclude'></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="goal">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<form style="margin-bottom: 0px">
|
||||
<label class="small">Goal</label>
|
||||
<input type="number" style="width:90%" ng-model="panel.query.goal" ng-change="set_refresh(true)">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span1">
|
||||
<label class="small"> Donut </label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
|
||||
<div class="row-fluid">
|
||||
<div class="span1">
|
||||
<label class="small"> Donut </label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Tilt </label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Labels </label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
|
||||
</div>
|
||||
<div class="span3">
|
||||
<label class="small">Legend</label>
|
||||
<select class="input-small" ng-model="panel.legend" ng-options="f for f in ['above','below','none']"></select></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Tilt </label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Labels </label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
|
||||
</div>
|
||||
<div class="span3">
|
||||
<label class="small">Legend</label>
|
||||
<select class="input-small" ng-model="panel.legend" ng-options="f for f in ['above','below','none']"></select></span>
|
||||
</div>
|
||||
</div>
|
||||
<h5>Panel Spy</h5>
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
|
||||
</div>
|
||||
<div class="span9 small">
|
||||
The panel spy shows 'behind the scenes' information about a panel. It can
|
||||
be accessed by clicking the <i class='icon-eye-open'></i> in the top right
|
||||
of the panel.
|
||||
<h5>Panel Spy</h5>
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
|
||||
</div>
|
||||
<div class="span9 small">
|
||||
The panel spy shows 'behind the scenes' information about a panel. It can
|
||||
be accessed by clicking the <i class='icon-eye-open'></i> in the top right
|
||||
of the panel.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -33,17 +33,17 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.pie', [])
|
||||
.controller('pie', function($scope, eventBus) {
|
||||
.controller('pie', function($scope, $rootScope, eventBus, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
status : "Deprecating Soon",
|
||||
query : { field:"_all", query:"*", goal: 1},
|
||||
query : { field:"_type", goal: 100},
|
||||
size : 10,
|
||||
exclude : [],
|
||||
donut : false,
|
||||
tilt : false,
|
||||
legend : true,
|
||||
legend : "above",
|
||||
labels : true,
|
||||
mode : "terms",
|
||||
group : "default",
|
||||
@ -53,30 +53,7 @@ angular.module('kibana.pie', [])
|
||||
_.defaults($scope.panel,_d)
|
||||
|
||||
$scope.init = function() {
|
||||
eventBus.register($scope,'time', function(event,time){set_time(time)});
|
||||
eventBus.register($scope,'query', function(event, query) {
|
||||
$scope.panel.query.query = _.isArray(query) ? query[0] : query;
|
||||
$scope.get_data();
|
||||
});
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
}
|
||||
|
||||
|
||||
$scope.remove_query = function(q) {
|
||||
if($scope.panel.mode !== 'query')
|
||||
return false;
|
||||
$scope.panel.query = _.without($scope.panel.query,q);
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
$scope.add_query = function(label,query) {
|
||||
if($scope.panel.mode !== 'query')
|
||||
return false;
|
||||
$scope.panel.query.unshift({
|
||||
query: query,
|
||||
label: label,
|
||||
});
|
||||
$scope.$on('refresh',function(){$scope.get_data()})
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
@ -84,21 +61,40 @@ angular.module('kibana.pie', [])
|
||||
switch(mode)
|
||||
{
|
||||
case 'terms':
|
||||
$scope.panel.query = {query:"*",field:"_all"};
|
||||
$scope.panel.query = {field:"_all"};
|
||||
break;
|
||||
case 'goal':
|
||||
$scope.panel.query = {query:"*",goal:100};
|
||||
$scope.panel.query = {goal:100};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.set_refresh = function (state) {
|
||||
$scope.refresh = state;
|
||||
}
|
||||
|
||||
$scope.close_edit = function() {
|
||||
if($scope.refresh)
|
||||
$scope.get_data();
|
||||
$scope.refresh = false;
|
||||
$scope.$emit('render');
|
||||
}
|
||||
|
||||
$scope.get_data = function() {
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
$scope.panel.loading = true;
|
||||
var request = $scope.ejs.Request().indices($scope.index);
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices);
|
||||
|
||||
// This could probably be changed to a BoolFilter
|
||||
var boolQuery = ejs.BoolQuery();
|
||||
_.each(query.list,function(q) {
|
||||
boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*'))
|
||||
})
|
||||
|
||||
// Terms mode
|
||||
if ($scope.panel.mode == "terms") {
|
||||
@ -109,10 +105,8 @@ angular.module('kibana.pie', [])
|
||||
.exclude($scope.panel.exclude)
|
||||
.facetFilter(ejs.QueryFilter(
|
||||
ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery($scope.panel.query.query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
boolQuery,
|
||||
filterSrv.getBoolFilter(filterSrv.ids)
|
||||
)))).size(0)
|
||||
|
||||
$scope.populate_modal(request);
|
||||
@ -141,11 +135,8 @@ angular.module('kibana.pie', [])
|
||||
// Goal mode
|
||||
} else {
|
||||
request = request
|
||||
.query(ejs.QueryStringQuery($scope.panel.query.query || '*'))
|
||||
.filter(ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
.cache(false))
|
||||
.query(boolQuery)
|
||||
.filter(filterSrv.getBoolFilter(filterSrv.ids))
|
||||
.size(0)
|
||||
|
||||
$scope.populate_modal(request);
|
||||
@ -169,26 +160,14 @@ angular.module('kibana.pie', [])
|
||||
$scope.modal = {
|
||||
title: "Inspector",
|
||||
body : "<h5>Last Elasticsearch Query</h5><pre>"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
|
||||
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
|
||||
angular.toJson(JSON.parse(request.toString()),true)+
|
||||
"'</pre>",
|
||||
}
|
||||
}
|
||||
|
||||
$scope.build_search = function(field,value) {
|
||||
$scope.panel.query.query = add_to_query($scope.panel.query.query,field,value,false)
|
||||
$scope.get_data();
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query.query]);
|
||||
}
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
})
|
||||
.directive('pie', function() {
|
||||
.directive('pie', function(query, filterSrv, dashboard) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
@ -228,8 +207,8 @@ angular.module('kibana.pie', [])
|
||||
show: scope.panel.labels,
|
||||
radius: 2/3,
|
||||
formatter: function(label, series){
|
||||
return '<div ng-click="build_search(panel.query.field,\''+label+'\') "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
|
||||
label+'<br/>'+Math.round(series.percent)+'%</div>';
|
||||
return '<div "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
|
||||
series.info.alias+'<br/>'+Math.round(series.percent)+'%</div>';
|
||||
},
|
||||
threshold: 0.1
|
||||
}
|
||||
@ -258,7 +237,7 @@ angular.module('kibana.pie', [])
|
||||
clickable: true
|
||||
},
|
||||
legend: { show: false },
|
||||
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
|
||||
colors: query.colors
|
||||
};
|
||||
|
||||
// Populate element
|
||||
@ -269,7 +248,7 @@ angular.module('kibana.pie', [])
|
||||
}
|
||||
}
|
||||
|
||||
function piett(x, y, contents) {
|
||||
function tt(x, y, contents) {
|
||||
var tooltip = $('#pie-tooltip').length ?
|
||||
$('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
|
||||
|
||||
@ -287,16 +266,19 @@ angular.module('kibana.pie', [])
|
||||
}
|
||||
|
||||
elem.bind("plotclick", function (event, pos, object) {
|
||||
if (!object)
|
||||
if (!object) {
|
||||
return;
|
||||
if(scope.panel.mode === 'terms')
|
||||
scope.build_search(scope.panel.query.field,object.series.label);
|
||||
}
|
||||
if(scope.panel.mode === 'terms') {
|
||||
filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label})
|
||||
dashboard.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
elem.bind("plothover", function (event, pos, item) {
|
||||
if (item) {
|
||||
var percent = parseFloat(item.series.percent).toFixed(1) + "%";
|
||||
piett(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +
|
||||
tt(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +
|
||||
(item.series.label||"")+ " " + percent);
|
||||
} else {
|
||||
$("#pie-tooltip").remove();
|
||||
|
@ -1,5 +1,15 @@
|
||||
<style>
|
||||
</style>
|
||||
<a class="close" ng-click="render();dismiss();" href="">×</a>
|
||||
<input class="input-medium" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' />
|
||||
<i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i>
|
||||
<div>
|
||||
<style>
|
||||
.input-query-alias {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
</style>
|
||||
<a class="close" ng-click="render();dismiss();" href="">×</a>
|
||||
<h6>Query Alias</h6>
|
||||
<form>
|
||||
<input class="input-medium input-query-alias" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' />
|
||||
<div>
|
||||
<i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -39,12 +39,10 @@ angular.module('kibana.query', [])
|
||||
}
|
||||
|
||||
$scope.refresh = function(query) {
|
||||
console.log('refresh')
|
||||
$rootScope.$broadcast('refresh')
|
||||
}
|
||||
|
||||
$scope.render = function(query) {
|
||||
console.log('render')
|
||||
$rootScope.$broadcast('render')
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.table', [])
|
||||
.controller('table', function($rootScope, $scope, eventBus, fields, query) {
|
||||
.controller('table', function($rootScope, $scope, eventBus, fields, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -61,10 +61,6 @@ angular.module('kibana.table', [])
|
||||
|
||||
$scope.set_listeners = function(group) {
|
||||
$scope.$on('refresh',function(){$scope.get_data()})
|
||||
eventBus.register($scope,'time',function(event,time) {
|
||||
$scope.panel.offset = 0;
|
||||
set_time(time)
|
||||
});
|
||||
eventBus.register($scope,'sort', function(event,sort){
|
||||
$scope.panel.sort = _.clone(sort);
|
||||
$scope.get_data();
|
||||
@ -112,26 +108,26 @@ angular.module('kibana.table', [])
|
||||
}
|
||||
|
||||
$scope.build_search = function(field,value,negate) {
|
||||
_.each(query.list,function(q) {
|
||||
q.query = add_to_query(q.query,field,value,negate);
|
||||
})
|
||||
var query = (negate ? '-':'+')+field+":\""+value+"\""
|
||||
filterSrv.set({type:'querystring',query:query})
|
||||
$scope.panel.offset = 0;
|
||||
$rootScope.$broadcast('refresh')
|
||||
dashboard.refresh();
|
||||
}
|
||||
|
||||
$scope.get_data = function(segment,query_id) {
|
||||
$scope.panel.error = false;
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
$scope.panel.loading = true;
|
||||
|
||||
var _segment = _.isUndefined(segment) ? 0 : segment
|
||||
$scope.segment = _segment;
|
||||
|
||||
var request = $scope.ejs.Request().indices($scope.index[_segment])
|
||||
var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
|
||||
|
||||
var boolQuery = ejs.BoolQuery();
|
||||
_.each(query.list,function(q) {
|
||||
@ -141,11 +137,8 @@ angular.module('kibana.table', [])
|
||||
request = request.query(
|
||||
ejs.FilteredQuery(
|
||||
boolQuery,
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to)
|
||||
)
|
||||
)
|
||||
filterSrv.getBoolFilter(filterSrv.ids)
|
||||
))
|
||||
.highlight(
|
||||
ejs.Highlight($scope.panel.highlight)
|
||||
.fragmentSize(2147483647) // Max size of a 32bit unsigned int
|
||||
@ -209,10 +202,10 @@ angular.module('kibana.table', [])
|
||||
// If we're not sorting in reverse chrono order, query every index for
|
||||
// size*pages results
|
||||
// Otherwise, only get size*pages results then stop querying
|
||||
if(
|
||||
($scope.data.length < $scope.panel.size*$scope.panel.pages ||
|
||||
!(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc')) &&
|
||||
_segment+1 < $scope.index.length
|
||||
if($scope.data.length < $scope.panel.size*$scope.panel.pages
|
||||
//($scope.data.length < $scope.panel.size*$scope.panel.pages
|
||||
// || !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc'))
|
||||
&& _segment+1 < dashboard.indices.length
|
||||
) {
|
||||
$scope.get_data(_segment+1,$scope.query_id)
|
||||
}
|
||||
@ -265,12 +258,6 @@ angular.module('kibana.table', [])
|
||||
}
|
||||
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
})
|
||||
.filter('highlight', function() {
|
||||
return function(text) {
|
||||
|
@ -8,40 +8,6 @@
|
||||
<input type="text" class="input-small" ng-model="panel.timefield">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<h5>Index Settings</h5>
|
||||
<div ng-show="panel.index_interval != 'none'" class="row-fluid">
|
||||
<div class="span12">
|
||||
<p class="small">
|
||||
Time stamped indices use your selected time range to create a list of
|
||||
indices that match a specified timestamp pattern. This can be very
|
||||
efficient for some data sets (eg, logs) For example, to match the
|
||||
default logstash index pattern you might use
|
||||
<code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are
|
||||
important as they instruct Kibana not to treat those letters as a
|
||||
pattern.
|
||||
</p>
|
||||
<p class="small">
|
||||
See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
|
||||
for documentation on date formatting.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<h6>Timestamp</h6><select class="input-mini" ng-model="panel.index_interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<h6>Index <span ng-show="panel.index_interval != 'none'">pattern <small>Absolutes in []</small></span></h6>
|
||||
<input type="text" class="input-medium" ng-model="panel.index">
|
||||
</div>
|
||||
<div class="span4">
|
||||
<h6>Failover Index <small>If index not found</small></h6>
|
||||
<input type="text" class="input-medium" ng-model="panel.defaultindex">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<h5>Relative mode <small>settings</small></h5>
|
||||
<div class="span6">
|
||||
|
@ -11,9 +11,6 @@
|
||||
* time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d']
|
||||
* timespan :: The default options selected for the relative view. Default: '15m'
|
||||
* timefield :: The field in which time is stored in the document.
|
||||
* index :: Index pattern to match. Literals should be double quoted. Default: '_all'
|
||||
* defaultindex :: Index to failover to if index not found
|
||||
* index_interval :: Time between timestamped indices (can be 'none') for static index
|
||||
* refresh: Object containing refresh parameters
|
||||
* enable :: true/false, enable auto refresh by default. Default: false
|
||||
* interval :: Seconds between auto refresh. Default: 30
|
||||
@ -28,7 +25,7 @@
|
||||
*/
|
||||
|
||||
angular.module('kibana.timepicker', [])
|
||||
.controller('timepicker', function($scope, eventBus, $timeout, timer, $http, kbnIndex) {
|
||||
.controller('timepicker', function($scope, $rootScope, eventBus, $timeout, timer, $http, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -37,9 +34,6 @@ angular.module('kibana.timepicker', [])
|
||||
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
|
||||
timespan : '15m',
|
||||
timefield : '@timestamp',
|
||||
index : '_all',
|
||||
defaultindex : "_all",
|
||||
index_interval: "none",
|
||||
timeformat : "",
|
||||
group : "default",
|
||||
refresh : {
|
||||
@ -58,6 +52,7 @@ angular.module('kibana.timepicker', [])
|
||||
// unnecessary refreshes during changes
|
||||
$scope.refresh_interval = $scope.panel.refresh.interval
|
||||
|
||||
|
||||
// Init a private time object with Date() objects depending on mode
|
||||
switch($scope.panel.mode) {
|
||||
case 'absolute':
|
||||
@ -86,35 +81,20 @@ angular.module('kibana.timepicker', [])
|
||||
if ($scope.panel.refresh.enable)
|
||||
$scope.set_interval($scope.panel.refresh.interval);
|
||||
|
||||
// In the case that a panel is not ready to receive a time event, it may
|
||||
// request one be sent by broadcasting a 'get_time' with its _id to its group
|
||||
// This panel can handle multiple groups
|
||||
eventBus.register($scope,"get_time", function(event,id) {
|
||||
eventBus.broadcast($scope.$id,id,'time',compile_time($scope.time))
|
||||
});
|
||||
|
||||
// In case some other panel broadcasts a time, set us to an absolute range
|
||||
eventBus.register($scope,"set_time", function(event,time) {
|
||||
$scope.panel.mode = 'absolute';
|
||||
set_timepicker(moment(time.from),moment(time.to))
|
||||
$scope.time_apply()
|
||||
});
|
||||
|
||||
eventBus.register($scope,"zoom", function(event,factor) {
|
||||
var _timespan = ($scope.time.to.valueOf() - $scope.time.from.valueOf());
|
||||
try {
|
||||
if($scope.panel.mode != 'absolute') {
|
||||
$scope.panel.mode = 'since'
|
||||
set_timepicker(moment($scope.time.to.valueOf() - _timespan*factor),$scope.time.to)
|
||||
} else {
|
||||
var _center = $scope.time.to.valueOf() - _timespan/2
|
||||
set_timepicker(moment(_center - (_timespan*factor)/2),
|
||||
moment(_center + (_timespan*factor)/2))
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
$scope.time_apply();
|
||||
$scope.$on('refresh', function() {
|
||||
var time = filterSrv.timeRange('min')
|
||||
|
||||
if($scope.time.from.diff(moment.utc(time.from)) != 0
|
||||
|| $scope.time.to.diff(moment.utc(time.to)) != 0)
|
||||
{
|
||||
$scope.panel.mode = 'absolute';
|
||||
|
||||
// These 3 statements basicly do everything time_apply() does
|
||||
set_timepicker(moment(time.from),moment(time.to))
|
||||
$scope.time = $scope.time_calc();
|
||||
update_panel()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -146,10 +126,26 @@ angular.module('kibana.timepicker', [])
|
||||
}
|
||||
}
|
||||
|
||||
var update_panel = function() {
|
||||
// Update panel's string representation of the time object.Don't update if
|
||||
// we're in relative mode since we dont want to store the time object in the
|
||||
// json for relative periods
|
||||
if($scope.panel.mode !== 'relative') {
|
||||
$scope.panel.time = {
|
||||
from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
|
||||
to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
|
||||
};
|
||||
} else {
|
||||
delete $scope.panel.time;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.set_mode = function(mode) {
|
||||
$scope.panel.mode = mode;
|
||||
$scope.panel.refresh.enable = mode === 'absolute' ?
|
||||
false : $scope.panel.refresh.enable
|
||||
|
||||
update_panel();
|
||||
}
|
||||
|
||||
$scope.to_now = function() {
|
||||
@ -201,54 +197,43 @@ angular.module('kibana.timepicker', [])
|
||||
};
|
||||
}
|
||||
|
||||
$scope.time_apply = function() {
|
||||
$scope.time_apply = function() {
|
||||
$scope.panel.error = "";
|
||||
// Update internal time object
|
||||
|
||||
// Remove all other time filters
|
||||
filterSrv.removeByType('time')
|
||||
|
||||
$scope.time = $scope.time_calc();
|
||||
$scope.time.field = $scope.panel.timefield
|
||||
update_panel()
|
||||
|
||||
// Get indices for the time period, then broadcast time range and index list
|
||||
// in a single object. Not sure if I like this.
|
||||
if($scope.panel.index_interval !== 'none') {
|
||||
kbnIndex.indices($scope.time.from,
|
||||
$scope.time.to,
|
||||
$scope.panel.index,
|
||||
$scope.panel.index_interval
|
||||
).then(function (p) {
|
||||
if(p.length > 0) {
|
||||
$scope.time.index = p;
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time))
|
||||
} else {
|
||||
$scope.panel.error = "Could not match index pattern to any ElasticSearch indices"
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$scope.time.index = [$scope.panel.index];
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time))
|
||||
}
|
||||
set_time_filter($scope.time)
|
||||
dashboard.refresh();
|
||||
|
||||
// Update panel's string representation of the time object.Don't update if
|
||||
// we're in relative mode since we dont want to store the time object in the
|
||||
// json for relative periods
|
||||
if($scope.panel.mode !== 'relative') {
|
||||
$scope.panel.time = {
|
||||
from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
|
||||
to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
|
||||
index : $scope.time.index,
|
||||
};
|
||||
} else {
|
||||
delete $scope.panel.time;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function set_time_filter(time) {
|
||||
time.type = 'time'
|
||||
// Check if there's a time filter we remember, if not, set one and remember it
|
||||
if(!_.isUndefined($scope.panel.filter_id) &&
|
||||
!_.isUndefined(filterSrv.list[$scope.panel.filter_id]) &&
|
||||
filterSrv.list[$scope.panel.filter_id].type == 'time')
|
||||
{
|
||||
filterSrv.set(compile_time(time),$scope.panel.filter_id)
|
||||
} else {
|
||||
$scope.panel.filter_id = filterSrv.set(compile_time(time))
|
||||
}
|
||||
return $scope.panel.filter_id;
|
||||
}
|
||||
|
||||
// Prefer to pass around Date() objects in the EventBus since interacting with
|
||||
// moment objects in libraries that are expecting Date()s can be tricky
|
||||
function compile_time(time) {
|
||||
time = _.clone(time)
|
||||
time.from = time.from.toDate()
|
||||
time.to = time.to.toDate()
|
||||
time.interval = $scope.panel.index_interval
|
||||
time.pattern = $scope.panel.index
|
||||
return time;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<span ng-class="{'text-success': query.hits.new >= query.hits.old, 'text-error': query.hits.old > query.hits.new}" class='strong'>
|
||||
<i class='large' ng-class="{'icon-caret-up': query.hits.new >= query.hits.old, 'icon-caret-down': query.hits.old > query.hits.new}"></i> {{query.percent}}%
|
||||
</span>
|
||||
<span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-show="query.label != ''">({{query.info.alias}})</span>
|
||||
<span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-show="query.info.alias != ''">({{query.info.alias}})</span>
|
||||
<br ng-show="panel.arrangement == 'vertical'">
|
||||
</div>
|
||||
</kibana-panel>
|
@ -19,7 +19,7 @@
|
||||
|
||||
*/
|
||||
angular.module('kibana.trends', [])
|
||||
.controller('trends', function($scope, eventBus, kbnIndex, query) {
|
||||
.controller('trends', function($scope, eventBus, kbnIndex, query, dashboard, filterSrv) {
|
||||
|
||||
// Set and populate defaults
|
||||
var _d = {
|
||||
@ -34,15 +34,12 @@ angular.module('kibana.trends', [])
|
||||
|
||||
$scope.init = function () {
|
||||
$scope.hits = 0;
|
||||
|
||||
$scope.$on('refresh',function(){$scope.get_data()})
|
||||
|
||||
eventBus.register($scope,'time', function(event,time){
|
||||
set_time(time)
|
||||
});
|
||||
eventBus.register($scope,'query', function(event, query) {
|
||||
$scope.panel.query = _.map(query,function(q) {
|
||||
return {query: q, label: q};
|
||||
})
|
||||
$scope.get_data();
|
||||
});
|
||||
// Now that we're all setup, request the time from our group
|
||||
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
|
||||
}
|
||||
@ -52,8 +49,11 @@ angular.module('kibana.trends', [])
|
||||
$scope.panel.loading = true;
|
||||
|
||||
// Make sure we have everything for the request to complete
|
||||
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
|
||||
if(dashboard.indices.length == 0) {
|
||||
return
|
||||
} else {
|
||||
$scope.index = dashboard.indices
|
||||
}
|
||||
|
||||
$scope.old_time = {
|
||||
from : new Date($scope.time.from.getTime() - interval_to_seconds($scope.panel.ago)*1000),
|
||||
@ -67,9 +67,8 @@ angular.module('kibana.trends', [])
|
||||
_.each(query.ids, function(id) {
|
||||
var q = $scope.ejs.FilteredQuery(
|
||||
ejs.QueryStringQuery(query.list[id].query || '*'),
|
||||
ejs.RangeFilter($scope.time.field)
|
||||
.from($scope.time.from)
|
||||
.to($scope.time.to))
|
||||
filterSrv.getBoolFilter(filterSrv.ids))
|
||||
|
||||
request = request
|
||||
.facet($scope.ejs.QueryFacet(id)
|
||||
.query(q)
|
||||
@ -110,7 +109,6 @@ angular.module('kibana.trends', [])
|
||||
// Populate scope when we have results
|
||||
function process_results(results) {
|
||||
results.then(function(results) {
|
||||
console.log(results)
|
||||
|
||||
$scope.panel.loading = false;
|
||||
if(_segment == 0) {
|
||||
@ -124,9 +122,17 @@ angular.module('kibana.trends', [])
|
||||
$scope.panel.error = $scope.parse_error(results.error);
|
||||
return;
|
||||
}
|
||||
if($scope.query_id === query_id) {
|
||||
|
||||
// Convert facet ids to numbers
|
||||
var facetIds = _.map(_.keys(results.facets),function(k){if(!isNaN(k)){return parseInt(k)}})
|
||||
|
||||
// Make sure we're still on the same query/queries
|
||||
if($scope.query_id === query_id &&
|
||||
_.intersection(facetIds,query.ids).length == query.ids.length
|
||||
) {
|
||||
var i = 0;
|
||||
_.each(query.ids, function(id) {
|
||||
var v = results.facets[id]
|
||||
var n = results.facets[id].count
|
||||
var o = results.facets['old_'+id].count
|
||||
|
||||
@ -193,7 +199,6 @@ angular.module('kibana.trends', [])
|
||||
|
||||
function set_time(time) {
|
||||
$scope.time = time;
|
||||
$scope.index = time.index || $scope.index
|
||||
$scope.get_data();
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,64 @@
|
||||
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<h4>Index Settings</h4>
|
||||
<div ng-show="dashboard.current.index.interval != 'none'" class="row-fluid">
|
||||
<div class="span12">
|
||||
<p class="small">
|
||||
Time stamped indices use your selected time range to create a list of
|
||||
indices that match a specified timestamp pattern. This can be very
|
||||
efficient for some data sets (eg, logs) For example, to match the
|
||||
default logstash index pattern you might use
|
||||
<code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are
|
||||
important as they instruct Kibana not to treat those letters as a
|
||||
pattern.
|
||||
</p>
|
||||
<p class="small">
|
||||
See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
|
||||
for documentation on date formatting.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<h6>Index <span ng-show="dashboard.current.index.interval != 'none'">pattern <small>Absolutes in []</small></span></h6>
|
||||
<input type="text" class="input-medium" ng-model="dashboard.current.index.pattern">
|
||||
</div>
|
||||
<div class="span4">
|
||||
<h6>Failover Index <small>If index not found</small></h6>
|
||||
<input type="text" class="input-medium" ng-model="dashboard.current.index.default">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<h4>Rows</h4>
|
||||
<div class="row-fluid">
|
||||
<form>
|
||||
<div class="span5">
|
||||
<label class="small">Title</label>
|
||||
<input type="text" class="input-large" ng-model='row.title'></input>
|
||||
</div>
|
||||
<div class="span2">
|
||||
<label class="small">Height</label>
|
||||
<input type="text" class="input-mini" ng-model='row.height'></input>
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Editable </label>
|
||||
<input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
|
||||
</div>
|
||||
<div class="span4">
|
||||
<label> </label>
|
||||
<button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<h4>Rows</h4>
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<th>Title</th>
|
||||
@ -30,23 +85,6 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h4>New row</h4>
|
||||
<div class="row-fluid">
|
||||
<div class="span8">
|
||||
<label class="small">Title</label>
|
||||
<input type="text" class="input-large" ng-model='row.title'></input>
|
||||
</div>
|
||||
<div class="span2">
|
||||
<label class="small">Height</label>
|
||||
<input type="text" class="input-mini" ng-model='row.height'></input>
|
||||
</div>
|
||||
<div class="span1">
|
||||
<label class="small"> Editable </label>
|
||||
<input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
|
||||
</div>
|
||||
</div>
|
||||
<button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button><br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();">Close</button>
|
||||
|
Loading…
Reference in New Issue
Block a user