Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Zachary Tong 2013-04-11 09:00:03 -04:00
commit a3c8b09547
30 changed files with 3311 additions and 1008 deletions

View File

@ -646,7 +646,7 @@ button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-
.nav>.disabled>a{color:#4d4d4d;}
.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;background-color:transparent;cursor:default;}
.navbar{overflow:visible;margin-bottom:20px;*position:relative;*z-index:2;}
.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#2e2e2e;background-image:-moz-linear-gradient(top, #333333, #262626);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#262626));background-image:-webkit-linear-gradient(top, #333333, #262626);background-image:-o-linear-gradient(top, #333333, #262626);background-image:linear-gradient(to bottom, #333333, #262626);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff333333', endColorstr='#ff262626', GradientType=0);border:1px solid #080808;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#2e2e2e;background-image:-moz-linear-gradient(top, #404040, #262626);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#404040), to(#262626));background-image:-webkit-linear-gradient(top, #404040, #262626);background-image:-o-linear-gradient(top, #404040, #262626);background-image:linear-gradient(to bottom, #404040, #262626);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404040', endColorstr='#ff262626', GradientType=0);border:1px solid #080808;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
.navbar-inner:after{clear:both;}
.navbar .container{width:auto;}
.nav-collapse.collapse{height:auto;overflow:visible;}

View File

@ -6,6 +6,50 @@
color: #000;
}
.kibana-row {
margin-left: 15px;
margin-bottom: 15px;
}
.navbar .brand {
color: #eee;
}
.navbar-inner {
border-width: 0 0 0px;
}
.row-close {
color: #bbb;
position: absolute;
font-size: 9pt;
font-weight: 200;
padding-left: 35px;
padding-top:0px;
}
.row-open {
text-align: right;
color: #bbb;
margin-top:30px;
position: absolute;
font-size: 13pt;
font-weight: 200;
-moz-transform-origin: 40px;
-ms-transform-origin: 40px;
-o-transform-origin: 40px;
-webkit-transform-origin: 40px;
transform-origin: 40px;
transform: rotate(-90deg);
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
}
.row-open i {
font-size: 10pt;
}
.odd {
background-color: #f9f9f9;
}
@ -95,4 +139,9 @@
background-color: #A60000;
}
.typeahead { z-index: 1051; }
.typeahead { z-index: 1051; }
.navbar-inner {
padding-left: 0px;
padding-right: 0px;
}

View File

@ -1,9 +1,10 @@
/*! elastic.js - v1.0.0 - 2013-01-15
/*! elastic.js - v1.0.0 - 2013-03-05
* https://github.com/fullscale/elastic.js
* Copyright (c) 2013 FullScale Labs, LLC; Licensed MIT */
/*jshint browser:true */
/*global angular:true */
/*jshint es5:true */
'use strict';
/*
@ -26,8 +27,8 @@ angular.module('elasticjs.service', [])
(successcb || angular.noop)(response.data);
return response.data;
}, function (response) {
(errorcb || angular.noop)(undefined);
return undefined;
(errorcb || angular.noop)(response.data);
return response.data;
});
};

View File

@ -1,4 +1,4 @@
/*! elastic.js - v1.0.0 - 2013-01-15
/*! elastic.js - v1.0.0 - 2013-03-05
* https://github.com/fullscale/elastic.js
* Copyright (c) 2013 FullScale Labs, LLC; Licensed MIT */
"use strict";angular.module("elasticjs.service",[]).factory("ejsResource",["$http",function(e){return function(t){var n=window.ejs||{},r=function(e,t,n){return e.then(function(e){return(t||angular.noop)(e.data),e.data},function(e){return(n||angular.noop)(undefined),undefined})};return t==null&&(t=""),n.client={server:function(e){return e==null?t:(t=e,this)},post:function(n,i,s,o){return n=t+n,r(e.post(n,i),s,o)},get:function(n,i,s,o){return n=t+n,r(e.get(n,i),s,o)},put:function(n,i,s,o){return n=t+n,r(e.put(n,i),s,o)},del:function(n,i,s,o){return n=t+n,r(e.delete(n,i),s,o)},head:function(n,r,i,s){return n=t+n,e.head(n,r).then(function(e){return(i||angular.noop)(e.headers()),e.headers()},function(e){return(s||angular.noop)(undefined),undefined})}},n}}]);
"use strict";angular.module("elasticjs.service",[]).factory("ejsResource",["$http",function(e){return function(t){var n=window.ejs||{},r=function(e,t,n){return e.then(function(e){return(t||angular.noop)(e.data),e.data},function(e){return(n||angular.noop)(undefined),undefined})};return t==null&&(t=""),n.client={server:function(e){return e==null?t:(t=e,this)},post:function(n,i,s,o){return n=t+n,r(e.post(n,i),s,o)},get:function(n,i,s,o){return n=t+n,r(e.get(n,i),s,o)},put:function(n,i,s,o){return n=t+n,r(e.put(n,i),s,o)},del:function(n,i,s,o){return n=t+n,r(e.delete(n,i),s,o)},head:function(n,r,i,s){return n=t+n,e.head(n,r).then(function(e){return(i||angular.noop)(e.headers()),e.headers()},function(e){return(s||angular.noop)(undefined),undefined})}},n}}]);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -36,13 +36,13 @@
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<p class="navbar-text pull-right"><small>Kibana 3 Preview</small></p>
<p class="navbar-text pull-right"><small><strong>Kibana 3</strong> <small>milestone 1</small></small></p>
<span class="brand">{{dashboards.title}}</span>
<div class="brand"><i class='icon-edit pointer' ng-show='dashboards.editable' bs-modal="'partials/dasheditor.html'"></i></div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="container-fluid main">
<div class="row-fluid">
<div ng-view></div>
</div>

View File

@ -27,7 +27,7 @@ var labjs = $LAB
.script("common/lib/angular-strap.min.js")
.script("common/lib/angular-sanitize.min.js")
.script("common/lib/elastic.min.js")
.script("common/lib/elastic-angular-client.min.js")
.script("common/lib/elastic-angular-client.js")
.script("common/lib/dateformat.js")
.script("common/lib/date.js")
.script("common/lib/datepicker.js")

View File

@ -3,7 +3,7 @@
'use strict';
angular.module('kibana.controllers', [])
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, ejsResource, eventBus) {
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, ejsResource, eventBus, fields) {
var _d = {
title: "",
@ -14,7 +14,11 @@ angular.module('kibana.controllers', [])
$scope.init = function() {
$scope.config = config;
// Make underscore.js available to views
$scope._ = _;
// Provide a global list of all see fields
$scope.fields = fields
$scope.reset_row();
$scope.clear_all_alerts();
@ -44,6 +48,10 @@ angular.module('kibana.controllers', [])
};
};
$scope.row_style = function(row) {
return { 'min-height': row.collapse ? '5px' : row.height }
}
$scope.alert = function(title,text,severity,timeout) {
var alert = {
title: title,
@ -70,6 +78,12 @@ angular.module('kibana.controllers', [])
return 'panels/'+type+'/editor.html';
}
// This is whoafully incomplete, but will do for now
$scope.parse_error = function(data) {
var _error = data.match("nested: (.*?);")
return _.isNull(_error) ? data : _error[1];
}
$scope.init();
})
@ -86,7 +100,7 @@ angular.module('kibana.controllers', [])
_.defaults($scope.row,_d)
$scope.init = function(){
$scope.init = function() {
$scope.reset_panel();
}
@ -94,13 +108,14 @@ angular.module('kibana.controllers', [])
row.collapse = row.collapse ? false : true;
if (!row.collapse) {
$timeout(function() {
$scope.send_render();
$scope.$broadcast('render')
});
}
}
$scope.send_render = function() {
$scope.$broadcast('render');
// This can be overridden by individual panel
$scope.close_edit = function() {
$scope.$broadcast('render')
}
$scope.add_panel = function(row,panel) {
@ -109,11 +124,11 @@ angular.module('kibana.controllers', [])
$scope.reset_panel = function() {
$scope.panel = {
loading: false,
error: false,
span: 3,
loading : false,
error : false,
span : 3,
editable: true,
group: ['default'],
group : ['default'],
};
};

View File

@ -67,6 +67,21 @@ angular.module('kibana.services', [])
});
}
})
/* Service: fields
Provides a global list of all seen fields for use in editor panels
*/
.factory('fields', function($rootScope) {
var fields = {
list : []
}
$rootScope.$on('fields', function(event,f) {
fields.list = _.union(f.data.all,fields.list)
})
return fields;
})
.service('timer', function($timeout) {
// This service really just tracks a list of $timeout promises to give us a

View File

@ -187,8 +187,9 @@ angular.module('kibana.dashcontrol', [])
var results = request.query(
$scope.ejs.QueryStringQuery(query || '*')
).size($scope.panel.elasticsearch_size).doSearch();
results.then(function(results) {
if(_.isUndefined(results)) {
if(_.isUndefined(results.hits)) {
return;
}
$scope.panel.error = false;

View File

@ -1,61 +1,73 @@
<div class="row-fluid" ng-controller='histogram'>
<div class="span3">
<form style="margin-bottom: 0px">
<h6>Label</h6>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
<div ng-controller='histogram'>
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<h6>Label</h6>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<h6>Query</h6>
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery=''"><i class="icon-plus"></i></button>
</form>
</div>
<div class="span1">
</div>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<h6>Query</h6>
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery=''"><i class="icon-plus"></i></button>
</form>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<input type="text" style="width:80%" ng-model="q.query">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
<div class="span1">
</div>
</div>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<input type="text" style="width:80%" ng-model="q.query">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<label class="small">Chart Options</label>
<select ng-change="$emit('render')" multiple style="width:95%" ng-model="panel.show" ng-options="f for f in ['bars','points','stack','lines','legend','x-axis','y-axis']"></select>
</div>
<div class="span3">
<label class="small">Line Fill (1 - 10)</label>
<input ng-change="$emit('render')" type="number" class="input-mini" ng-model="panel.fill">
</div>
<div class="span3">
<label class="small">Time correction</label>
<select ng-change="$emit('render')" ng-model="panel.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
</div>
<div class="span2">
<label class="small">Zoom Links</label><input type="checkbox" ng-model="panel.zoomlinks" ng-checked="panel.zoomlinks">
</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>Chart Options</h5>
<div class="row-fluid" style="margin-bottom:10px;">
<div class="span1"> <label class="small">Bars</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel.bars" ng-checked="panel.bars"></div>
<div class="span1"> <label class="small">Lines</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel.lines" ng-checked="panel.lines"></div>
<div class="span1"> <label class="small">Points</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel.points" ng-checked="panel.points"></div>
<div class="span1"> <label class="small">Stack</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel.stack" ng-checked="panel.stack"></div>
<div class="span1"> <label class="small">Legend</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel.legend" ng-checked="panel.legend"></div>
<div class="span1"> <label class="small">xAxis</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel['x-axis']" ng-checked="panel['x-axis']"></div>
<div class="span1"> <label class="small">yAxis</label><input ng-change="$emit('render')" type="checkbox" ng-model="panel['y-axis']" ng-checked="panel['y-axis']"></div>
<div class="span2" ng-show="panel.lines">
<label class="small">Line Fill</label>
<select ng-change="$emit('render')" class="input-mini" ng-model="panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="span2" ng-show="panel.lines">
<label class="small">Line Width</label>
<select ng-change="$emit('render')" class="input-mini" ng-model="panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<label class="small">Time correction</label>
<select ng-change="$emit('render')" ng-model="panel.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
</div>
<div class="span2">
<label class="small">Zoom Links</label><input type="checkbox" ng-model="panel.zoomlinks" ng-checked="panel.zoomlinks">
</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.
</div>
</div>
</div>

View File

@ -2,15 +2,16 @@
<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>
<center ng-show='panel.zoomlinks && data'>
<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(2)'><i class='icon-zoom-out'></i> Zoom Out</a>
</center>
<div>
<span ng-repeat='series in legend' style='display:inline-block;padding-right:5px'>
<div style="display:inline-block;background:{{series.color}};height:10px;width:10px"></div>
</span> |
<span ng-show="panel.legend" ng-repeat='series in plot.getData()' style='display:inline-block;padding-right:5px'>
<div style="display:inline-block;background:{{series.color}};height:10px;width:10px;border-radius:5px;"></div>
<div class='small' style='display:inline-block'>{{series.label}} ({{series.hits}})</div>
</span><span class="small"> per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> total)</span>
</div>
<div histogram params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
<center><img ng-show='panel.loading && _.isUndefined(data)' src="common/img/load_big.gif"></center>
<div histogram-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
</kibana-panel>

View File

@ -3,19 +3,28 @@ angular.module('kibana.histogram', [])
// Set and populate defaults
var _d = {
group : "default",
query : [ {query: "*", label:"Query"} ],
interval : secondsToHms(calculate_interval($scope.from,$scope.to,40,0)/1000),
show : ['bars','y-axis','x-axis','legend'],
fill : 3,
linewidth : 3,
timezone : 'browser', // browser, utc or a standard timezone
spyable : true,
zoomlinks : true,
group : "default",
bars : true,
stack : true,
points : false,
lines : false,
legend : true,
'x-axis' : true,
'y-axis' : true,
}
_.defaults($scope.panel,_d)
$scope.init = function() {
eventBus.register($scope,'time', function(event,time){$scope.set_time(time)});
// Consider eliminating the check for array, this should always be an array
eventBus.register($scope,'query', function(event, query) {
if(_.isArray(query)) {
$scope.panel.query = _.map(query,function(q) {
@ -26,6 +35,7 @@ angular.module('kibana.histogram', [])
}
$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))
@ -48,13 +58,13 @@ angular.module('kibana.histogram', [])
}
$scope.get_data = function(segment,query_id) {
delete $scope.panel.error
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
return
var _segment = _.isUndefined(segment) ? 0 : segment
$scope.panel.loading = true;
var _segment = _.isUndefined(segment) ? 0 : segment
var request = $scope.ejs.Request().indices($scope.panel.index[_segment]);
// Build the question part of the query
@ -68,7 +78,7 @@ angular.module('kibana.histogram', [])
)
});
// Build the facet part
// Build the facet part, injecting the query in as a facet filter
_.each(queries, function(v) {
request = request
.facet($scope.ejs.DateHistogramFacet("chart"+_.indexOf(queries,v))
@ -78,6 +88,7 @@ angular.module('kibana.histogram', [])
).size(0)
})
// Populate the inspector panel
$scope.populate_modal(request);
// Then run it
@ -92,16 +103,17 @@ angular.module('kibana.histogram', [])
query_id = $scope.query_id = new Date().getTime();
}
// Check for error and abort if found
if(!(_.isUndefined(results.error))) {
$scope.panel.error = $scope.parse_error(results.error);
return;
}
// Make sure we're still on the same query
if($scope.query_id === query_id) {
var i = 0;
_.each(results.facets, function(v, k) {
// If this isn't a date histogram it must be a QueryFacet, get the
// count and return
if(v._type !== 'date_histogram') {
//$scope.hits += v.count;
return
}
// Null values at each end of the time range ensure we see entire range
if(_.isUndefined($scope.data[i]) || _segment == 0) {
@ -116,14 +128,14 @@ angular.module('kibana.histogram', [])
var segment_data = [];
_.each(v.entries, function(v, k) {
segment_data.push([v['time'],v['count']])
hits += v['count'];
$scope.hits += v['count'];
hits += v['count']; // The series level hits counter
$scope.hits += v['count']; // Entire dataset level hits counter
});
data.splice.apply(data,[1,0].concat(segment_data))
data.splice.apply(data,[1,0].concat(segment_data)) // Join histogram data
// Create the flot series
// Create the flot series object
var series = {
data: {
label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
@ -140,8 +152,10 @@ angular.module('kibana.histogram', [])
i++;
});
eventBus.broadcast($scope.$id,$scope.panel.group,'hits',$scope.hits)
// Tell the histogram directive to render.
$scope.$emit('render')
// If we still have segments left, get them
if(_segment < $scope.panel.index.length-1) {
$scope.get_data(_segment+1,query_id)
}
@ -176,15 +190,13 @@ angular.module('kibana.histogram', [])
}
})
.directive('histogram', function(eventBus) {
.directive('histogramChart', function(eventBus) {
return {
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
var height = scope.panel.height || scope.row.height;
elem.html('<center><img src="common/img/load_big.gif"></center>')
// Receive render events
scope.$on('render',function(){
render_panel();
@ -197,19 +209,6 @@ angular.module('kibana.histogram', [])
// Function for rendering panel
function render_panel() {
// Determine format
var show = _.isUndefined(scope.panel.show) ? {
bars: true, lines: false, points: false
} : {
lines: _.indexOf(scope.panel.show,'lines') < 0 ? false : true,
bars: _.indexOf(scope.panel.show,'bars') < 0 ? false : true,
points: _.indexOf(scope.panel.show,'points') < 0 ? false : true,
stack: _.indexOf(scope.panel.show,'stack') < 0 ? null : true,
legend: _.indexOf(scope.panel.show,'legend') < 0 ? false : true,
'x-axis': _.indexOf(scope.panel.show,'x-axis') < 0 ? false : true,
'y-axis': _.indexOf(scope.panel.show,'y-axis') < 0 ? false : true,
}
// Set barwidth based on specified interval
var barwidth = interval_to_seconds(scope.panel.interval)*1000
@ -221,31 +220,36 @@ angular.module('kibana.histogram', [])
// Populate element. Note that jvectormap appends, does not replace.
scripts.wait(function(){
var stack = scope.panel.stack ? true : null;
// Populate element
try {
var plot = $.plot(elem, scope.data, {
legend: {
show: false,
},
scope.plot = $.plot(elem, scope.data, {
legend: { show: false },
series: {
stack: show.stack,
lines: { show: show.lines, fill: scope.panel.fill/10 },
bars: { show: show.bars, fill: 1, barWidth: barwidth/1.8 },
points: { show: show.points, fill: 1, fillColor: false},
stack: stack,
lines: {
show: scope.panel.lines,
fill: scope.panel.fill/10,
lineWidth: scope.panel.linewidth,
steps: false
},
bars: { show: scope.panel.bars, fill: 1, barWidth: barwidth/1.8 },
points: { show: scope.panel.points, fill: 1, fillColor: false, radius: 5},
shadowSize: 1
},
yaxis: { show: show['y-axis'], min: 0, color: "#000" },
yaxis: { show: scope.panel['y-axis'], min: 0, color: "#000" },
xaxis: {
timezone: scope.panel.timezone,
show: show['x-axis'],
show: scope.panel['x-axis'],
mode: "time",
timeformat: time_format(scope.panel.interval),
label: "Datetime",
color: "#000",
},
selection: {
mode: "x"
mode: "x",
color: '#666'
},
grid: {
backgroundColor: '#fff',
@ -254,12 +258,13 @@ angular.module('kibana.histogram', [])
color: "#eee",
hoverable: true,
},
colors: ['#EB6841','#00A0B0','#6A4A3C','#EDC951','#CC333F']
})
scope.legend = [];
_.each(plot.getData(),function(series) {
scope.legend.push(_.pick(series,'label','color','hits'))
colors: ['#86B22D',
'#BF6730',
'#1D7373',
'#BFB930',
'#BF3030',
'#77207D'
]
})
// Work around for missing legend at initialization
@ -288,23 +293,25 @@ angular.module('kibana.histogram', [])
var tooltip = $('#pie-tooltip').length ?
$('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
//var tooltip = $('#pie-tooltip')
tooltip.text(contents).css({
tooltip.html(contents).css({
position: 'absolute',
top : y + 5,
left : x + 5,
color : "#FFF",
border : '1px solid #FFF',
padding : '2px',
'font-size': '8pt',
'background-color': '#000',
color : "#000",
border : '2px solid #000',
padding : '10px',
'font-size': '11pt',
'font-weight' : 200,
'background-color': '#FFF',
'border-radius': '10px',
}).appendTo("body");
}
elem.bind("plothover", function (event, pos, item) {
if (item) {
var percent = parseFloat(item.series.percent).toFixed(1) + "%";
tt(pos.pageX, pos.pageY,
item.datapoint[1].toFixed(1) + " @ " +
"<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> "+
item.datapoint[1].toFixed(0) + " @ " +
new Date(item.datapoint[0]).format('mm/dd HH:MM:ss'));
} else {
$("#pie-tooltip").remove();

View File

@ -1,23 +1,49 @@
<div class="row-fluid" ng-controller="hits">
<div ng-controller="hits">
<div class="row-fluid">
<div class="span2 "><label class="small">Font Size</label>
<select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
</div>
<div class="span2">
<label class="small">Run Query</label><input type="checkbox" ng-model="panel.run_query" ng-checked="panel.run_query">
<label class="small">Aggregate</label><input type="checkbox" ng-model="panel.aggregate" ng-checked="panel.aggregate">
</div>
<div class="span9" ng-show='!panel.run_query'>
With query running disabled, this panel receives its hit count from a histogram panel. If multiple queries are running this <strong>will show the total of all queries</strong>.
<div class="span3" ng-show="!panel.aggregate"><label class="small">Counter Style</label>
<select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['none','horizontal','vertical']"></select></span>
</div>
<div class="span9" ng-show='panel.run_query'>
This shows a simple count of how many records match your filtered query. If multiple queries are sent from a single panel the <strong>first query will be displayed</strong>
<div class="span2" ng-show="!panel.aggregate">
<label class="small">Chart</label><input type="checkbox" ng-model="panel.chart" ng-checked="panel.chart">
</div>
</div>
<div class="row-fluid">
<div class="span9" ng-show='panel.run_query'>
<form class="input-append">
<h6>Query</h6>
<input type="text" style="width:85%" ng-model="panel.query">
<button class="btn" ng-click="get_data();"><i class="icon-search"></i></button>
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<label class="small">Label</label>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
</div>
<div class="span3"><h6>Font Size</h6>
<select class="input-small" ng-model="panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<label class="small">Query</label>
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery=''"><i class="icon-plus"></i></button>
</form>
</div>
<div class="span1">
</div>
</div>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<input type="text" style="width:80%" ng-model="q.query">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
</div>

View File

@ -1,3 +1,14 @@
<kibana-panel ng-controller='hits' ng-init="init()">
<p ng-style="panel.style">&#8805 {{hits}}</p>
<div ng-show="panel.counters">
<p ng-style="panel.style" ng-show="panel.aggregate">{{hits}}</p>
<table ng-style="panel.style" ng-show="!panel.aggregate && panel.arrangement == 'vertical'">
<tr style="line-height:{{panel.style['font-size']}}" ng-repeat="query in plot.getData()">
<td ng-show="panel.chart" style="background:{{query.color}};width:{{panel.style['font-size']}}"></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.hits}}</td>
</tr>
</table>
<div ng-style="panel.style" ng-show="!panel.aggregate && panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
<span ng-show='panel.chart'><div style="display:inline-block;border-radius:{{panel.style['font-size']}};background:{{query.color}};height:{{panel.style['font-size']}};width:{{panel.style['font-size']}}"></div></span> {{query.label}} ({{query.hits}}) <span ng-show="!$last">|</span>
</div><br>
</div><div style="clear:both"></div>
<div ng-show='panel.chart && !panel.aggregate ' hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
</kibana-panel>

View File

@ -5,64 +5,207 @@ angular.module('kibana.hits', [])
var _d = {
query : "*",
group : "default",
style : { "font-size": '36pt', "font-weight": "bold" },
run_query : false
style : { "font-size": '10pt'},
aggregate : true,
arrangement : 'vertical',
chart : true,
counters: true,
count_pos: 'above'
}
_.defaults($scope.panel,_d)
$scope.init = function () {
$scope.hits = 0;
eventBus.register($scope,'time', function(event,time){
if($scope.panel.run_query)
set_time(time)
set_time(time)
});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.isArray(query) ? query[0] : query;
if($scope.panel.run_query)
$scope.get_data();
$scope.panel.query = _.map(query,function(q) {
return {query: q, label: q};
})
$scope.get_data();
});
eventBus.register($scope,'hits', function(event, hits) {
$scope.hits = hits;
})
// 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() {
$scope.get_data = function(segment,query_id) {
delete $scope.panel.error
$scope.panel.loading = true;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
return
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.panel.index);
var results = request
.query(ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
var _segment = _.isUndefined(segment) ? 0 : segment
var request = $scope.ejs.Request().indices($scope.panel.index[_segment]);
// Build the question part of the query
var queries = [];
_.each($scope.panel.query, function(v) {
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
)
.to($scope.time.to))
)
.size(0)
.doSearch();
});
// Build the facet part
_.each(queries, function(v) {
request = request
.facet($scope.ejs.QueryFacet("query"+_.indexOf(queries,v))
.query(v)
).size(0)
})
// TODO: Spy for hits panel
//$scope.populate_modal(request);
// Then run it
var results = request.doSearch();
// Populate scope when we have results
results.then(function(results) {
$scope.panel.loading = false;
if(_.isUndefined(results)) {
$scope.panel.error = 'Your query was unsuccessful';
if(_segment == 0) {
$scope.hits = 0;
$scope.data = [];
query_id = $scope.query_id = new Date().getTime();
}
// Check for error and abort if found
if(!(_.isUndefined(results.error))) {
$scope.panel.error = $scope.parse_error(results.error);
return;
}
$scope.panel.error = false;
$scope.hits = results.hits.total;
if($scope.query_id === query_id) {
var i = 0;
_.each(results.facets, function(v, k) {
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),
hits: hits,
data: [[i,hits]]
};
i++;
});
$scope.$emit('render');
if(_segment < $scope.panel.index.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();
}
function set_time(time) {
$scope.time = time;
$scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index
$scope.get_data();
}
}).directive('hitsChart', function(eventBus) {
return {
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
// Receive render events
scope.$on('render',function(){
render_panel();
});
// Re-render if the window is resized
angular.element(window).bind('resize', function(){
render_panel();
});
// Function for rendering panel
function render_panel() {
var scripts = $LAB.script("common/lib/panels/jquery.flot.js")
// Populate element. Note that jvectormap appends, does not replace.
scripts.wait(function(){
// Populate element
try {
// Add plot to scope so we can build out own legend
scope.plot = $.plot(elem, scope.data, {
legend: { show: false },
series: {
lines: { show: false, },
bars: { show: true, fill: 1, barWidth: 0.8, horizontal: false },
shadowSize: 1
},
yaxis: { show: true, min: 0, color: "#000" },
xaxis: { show: false },
grid: {
backgroundColor: '#fff',
borderWidth: 0,
borderColor: '#eee',
color: "#eee",
hoverable: true,
},
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
})
// Work around for missing legend at initialization
if(!scope.$$phase)
scope.$apply()
} catch(e) {
elem.text(e)
}
})
}
function tt(x, y, contents) {
var tooltip = $('#pie-tooltip').length ?
$('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
//var tooltip = $('#pie-tooltip')
tooltip.html(contents).css({
position: 'absolute',
top : y + 5,
left : x + 5,
color : "#000",
border : '2px solid #000',
padding : '10px',
'font-size': '11pt',
'font-weight' : 200,
'background-color': '#FFF',
'border-radius': '10px',
}).appendTo("body");
}
elem.bind("plothover", function (event, pos, item) {
if (item) {
tt(pos.pageX, pos.pageY,
"<div style='vertical-align:middle;border-radius:10px;display:inline-block;background:"+item.series.color+";height:20px;width:20px'></div> "+
item.datapoint[1].toFixed(0))
} else {
$("#pie-tooltip").remove();
}
});
}
};
})

View File

@ -8,7 +8,7 @@
<div class="span3">
<form>
<h6>Field</h6>
<input type="text" class="input-small" ng-model="panel.field">
<input bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.field">
</form>
</div>
<div class="span6">

View File

@ -128,12 +128,12 @@ angular.module('kibana.map', [])
$('.jvectormap-label').css({
"position" : "absolute",
"display" : "none",
"border" : "solid 1px #CDCDCD",
"background" : "#292929",
"color" : "white",
"font-family" : "sans-serif, Verdana",
"font-size" : "smaller",
"padding" : "3px"
"border" : "solid 2px #000",
"background" : "#FFF",
"font-weight" : 200,
"border-radius": "5px",
"color" : "#000",
"padding" : "5px"
})
var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
$('.jvectormap-label').text(label.text() + ": " + count);

View File

@ -4,7 +4,7 @@
<div class="span4">
<form style="margin-bottom: 0px">
<h6> Field</h6>
<input type="text" style="width:90%" ng-model="panel.query.field">
<input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field">
</form>
</div>
<div class="span8">

View File

@ -2,6 +2,11 @@
<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>
<span ng-show="panel.legend" ng-repeat='series in legend' style='padding-right:5px'>
<div style='white-space:nowrap;display:table-cell'>
<div style="display:inline-block;background:{{series.color}};height:10px;width:10px;border-radius:5px;"></div>
<div class='small' style='display:inline-block'>{{series.label}} ({{series.percent}}%)</div>
</div>
</span>
<div pie params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
</kibana-panel>

View File

@ -268,14 +268,21 @@ angular.module('kibana.pie', [])
},
//grid: { hoverable: true, clickable: true },
grid: { hoverable: true, clickable: true },
legend: { show: scope.panel.legend },
colors: ['#EB6841','#00A0B0','#6A4A3C','#EDC951','#CC333F']
legend: { show: false },
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
};
// Populate element
if(elem.is(":visible")){
scripts.wait(function(){
$.plot(elem, scope.data, pie);
var plot = $.plot(elem, scope.data, pie);
scope.legend = [];
_.each(plot.getData(),function(series) {
var item = _.pick(series,'label','color','percent')
item.percent = parseFloat(item.percent).toFixed(1)
scope.legend.push(item)
})
console.log(scope.legend)
});
}
}
@ -283,15 +290,16 @@ angular.module('kibana.pie', [])
function piett(x, y, contents) {
var tooltip = $('#pie-tooltip').length ?
$('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
tooltip.text(contents).css({
tooltip.html(contents).css({
position: 'absolute',
top : y + 10,
left : x + 10,
color : "#FFF",
border : '1px solid #FFF',
padding : '2px',
'font-size': '8pt',
'background-color': '#000',
color : "#000",
'font-weight': 200,
'border-radius': '5px',
border : '2px solid #000',
padding : '10px',
'background-color': '#FFF',
}).appendTo("body");
}
@ -305,7 +313,8 @@ angular.module('kibana.pie', [])
elem.bind("plothover", function (event, pos, item) {
if (item) {
var percent = parseFloat(item.series.percent).toFixed(1) + "%";
piett(pos.pageX, pos.pageY, percent + " " + (item.series.label||""));
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> " +
(item.series.label||"")+ " " + percent);
} else {
$("#pie-tooltip").remove();
}

View File

@ -21,7 +21,7 @@
</form>
</div>
<div class='row-fluid' ng-show="panel.multi && panel.multi_arrange == 'horizontal'">
<form class="form-inline" style="width:100%;" >
<form class="form-inline" style="width:100%;margin:0px" >
<span ng-repeat="q in panel.query">
<span class="input-append" style="margin-bottom:0px;margin-right:5px">
<button class="btn btn-danger" type="submit" style="width:50px;margin-left:-50px;visibility:hidden"></button>
@ -31,7 +31,7 @@
</span>
</form>
<button type="submit" class="btn btn-info" ng-click="send_query(panel.query)"><i class="icon-search"></i> Search</button>
<button type="submit" class="btn" ng-click="send_query(panel.query);add_query();"><i class="icon-plus"></i> Add Query</button>
<button type="submit" class="btn" ng-click="add_query();"><i class="icon-plus"></i> Add Query</button>
</div>
<div ng-show="panel.multi && panel.multi_arrange == 'vertical'">
<form>

View File

@ -11,7 +11,7 @@
<div class="span4">
<form class="input-append">
<h6>Add field</h6>
<input bs-typeahead="all_fields" type="text" class="input-small" ng-model='newfield'>
<input bs-typeahead="fields.list" type="text" class="input-small" ng-model='newfield'>
<button class="btn" ng-click="toggle_field(newfield);newfield=''"><i class="icon-plus"></i></button>
</form>
</div>

View File

@ -1,5 +1,5 @@
angular.module('kibana.table', [])
.controller('table', function($scope, eventBus) {
.controller('table', function($scope, eventBus, fields) {
// Set and populate defaults
var _d = {
@ -76,6 +76,8 @@ angular.module('kibana.table', [])
}
$scope.get_data = function(segment,query_id) {
$scope.panel.error = false;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
return
@ -98,7 +100,7 @@ angular.module('kibana.table', [])
$scope.populate_modal(request)
var results = request.doSearch();
var results = request.doSearch()
// Populate scope when we have results
results.then(function(results) {
@ -110,11 +112,11 @@ angular.module('kibana.table', [])
query_id = $scope.query_id = new Date().getTime()
}
if(_.isUndefined(results)) {
$scope.panel.error = 'Your query was unsuccessful';
// Check for error and abort if found
if(!(_.isUndefined(results.error))) {
$scope.panel.error = $scope.parse_error(results.error);
return;
}
$scope.panel.error = false;
// Check that we're still on the same query, if not stop
if($scope.query_id === query_id) {
@ -142,7 +144,6 @@ angular.module('kibana.table', [])
// This breaks, use $scope.data for this
$scope.all_fields = get_all_fields($scope.data);
broadcast_results();
// If we're not sorting in reverse chrono order, query every index for

View File

@ -23,7 +23,7 @@
</div>
</div>
<div ng-switch-when="since">
<div class="span10">
<div class="span5">
<form class="nomargin">
<label><small>Since</small></label>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.from.date" data-date-format="mm/dd/yyyy" bs-datepicker>

View File

@ -111,10 +111,6 @@ angular.module('kibana.timepicker', [])
}
$scope.time_apply();
});
$scope.$on('render', function (){
$scope.time_apply();
});
}
$scope.set_interval = function (refresh_interval) {
@ -167,6 +163,10 @@ angular.module('kibana.timepicker', [])
$scope.time_apply();
}
$scope.close_edit = function() {
$scope.time_apply();
}
$scope.time_check = function(){
// If time picker is defined (on initialization)

View File

@ -1,17 +1,21 @@
<div class="row-fluid container">
<div class="row-fluid container" style="margin-top:10px">
<!-- Rows -->
<div ng-controller="dashcontrol" ng-init="init()"></div>
<div class="row-fluid" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboards.rows">
<div class="span12">
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboards.rows" ng-style="row_style(row)">
<div class="row-control">
<div class="row-fluid row-header" style="padding:0px;margin:0px;height:0px">
<div class="span12" style="min-height:5px;vertical-align:bottom">
<i ng-show="row.editable" class="icon-edit pointer editlink" bs-modal="'partials/roweditor.html'"></i>
<span ng-show="row.collapsable" ng-click="toggle_row(row)" class="pointer"><i class="pointer" ng-class="{'icon-caret-down': !row.collapse,'icon-caret-right': row.collapse}"></i> <small>{{row.title}}</small></span>
<small ng-hide="row.collapsable">{{row.title}}</small>
<div style="vertical-align:bottom">
<div ng-show="row.collapsable">
<div ng-class="{'row-open': !row.collapse, 'row-close': row.collapse}" style="position:absolute;margin-left:-60px;">
<span class='pointer' ng-click="toggle_row(row)">{{row.title}}</span>
<i ng-show="row.editable" class="icon-edit pointer editlink" bs-modal="'partials/roweditor.html'"></i>
</div>
</div>
<small ng-hide="row.collapsable" class="rotated">{{row.title}}</small>
</div>
</div>
<div class="row-fluid" style="padding-top:10px" ng-hide="row.collapse">
<div class="row-fluid" style="padding-top:0px" ng-hide="row.collapse">
<!-- Panels -->
<div ng-repeat="(name, panel) in row.panels" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel" style="min-height:{{row.height}}; position:relative">
<!-- Error Panel -->

View File

@ -1,6 +1,6 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>{{panel.title}} <small>editor</small></h3>
<h3>{{panel.title}} Editor</h3>
</div>
<div class="modal-body">
@ -13,5 +13,5 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();send_render()">Close</button>
<button type="button" class="btn btn-success" ng-click="dismiss();close_edit()">Close</button>
</div>

View File

@ -56,5 +56,5 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();send_render()">Close</button>
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();close_edit()">Close</button>
</div>