Преглед изворни кода

Merge remote-tracking branch 'upstream/master'

Zachary Tong пре 12 година
родитељ
комит
a3c8b09547

+ 1 - 1
common/css/bootstrap.min.css

@@ -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;}

+ 50 - 1
common/css/main.css

@@ -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;
+}

+ 4 - 3
common/lib/elastic-angular-client.js

@@ -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;
         });
       };
 

+ 2 - 2
common/lib/elastic-angular-client.min.js

@@ -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}}]);

Разлика између датотеке није приказан због своје велике величине
+ 373 - 266
common/lib/elastic.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
common/lib/elastic.min.js


+ 2 - 2
index.html

@@ -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>

+ 1 - 1
js/app.js

@@ -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")

+ 24 - 9
js/controllers.js

@@ -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'],
     };
   };
 

+ 15 - 0
js/services.js

@@ -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

+ 2 - 1
panels/dashcontrol/module.js

@@ -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;

+ 68 - 56
panels/histogram/editor.html

@@ -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">
+  <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>
-</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>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>

+ 7 - 6
panels/histogram/module.html

@@ -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>         

+ 64 - 57
panels/histogram/module.js

@@ -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();

+ 41 - 15
panels/hits/editor.html

@@ -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="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="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="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="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="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="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="span1">
     </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" 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="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">
+        <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>

+ 12 - 1
panels/hits/module.html

@@ -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>         

+ 168 - 25
panels/hits/module.js

@@ -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();
+        }
+      });
+
+    }
+  };
 })

+ 1 - 1
panels/map/editor.html

@@ -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">

+ 6 - 6
panels/map/module.js

@@ -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);

+ 1 - 1
panels/pie/editor.html

@@ -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">

+ 6 - 1
panels/pie/module.html

@@ -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>         

+ 19 - 10
panels/pie/module.js

@@ -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();
         }

+ 2 - 2
panels/stringquery/module.html

@@ -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>

+ 1 - 1
panels/table/editor.html

@@ -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>

+ 7 - 6
panels/table/module.js

@@ -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

+ 1 - 1
panels/timepicker/module.html

@@ -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>  

+ 4 - 4
panels/timepicker/module.js

@@ -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)

+ 12 - 8
partials/dashboard.html

@@ -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 -->

+ 2 - 2
partials/paneleditor.html

@@ -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>

+ 1 - 1
partials/roweditor.html

@@ -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>

Неке датотеке нису приказане због велике количине промена