瀏覽代碼

Merge pull request #33 from rashidkpc/master

More prettiness, and a bar chart for the hits panel
Rashid Khan 12 年之前
父節點
當前提交
f0faf66c0e

+ 40 - 0
common/css/main.css

@@ -6,6 +6,46 @@
   color: #000;
   color: #000;
 }
 }
 
 
+.kibana-row {
+  margin-left: 15px;
+  margin-bottom: 15px;
+}
+
+.navbar .brand {
+  color: #eee;
+}
+
+.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 {
 .odd {
   background-color: #f9f9f9;
   background-color: #f9f9f9;
 }
 }

+ 1 - 1
index.html

@@ -36,7 +36,7 @@
   <div class="navbar navbar-static-top">
   <div class="navbar navbar-static-top">
     <div class="navbar-inner">
     <div class="navbar-inner">
       <div class="container-fluid">
       <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>
         <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 class="brand"><i class='icon-edit pointer' ng-show='dashboards.editable' bs-modal="'partials/dasheditor.html'"></i></div>
       </div>
       </div>

+ 62 - 56
panels/histogram/editor.html

@@ -1,61 +1,67 @@
-<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>
-  <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>
-  <div class="span1">
+  <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="span2">
+      <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">
+      <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 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>
-<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>
 </div>
 </div>

+ 4 - 4
panels/histogram/module.html

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

+ 20 - 12
panels/histogram/module.js

@@ -7,6 +7,7 @@ angular.module('kibana.histogram', [])
     interval  : secondsToHms(calculate_interval($scope.from,$scope.to,40,0)/1000),
     interval  : secondsToHms(calculate_interval($scope.from,$scope.to,40,0)/1000),
     show      : ['bars','y-axis','x-axis','legend'],
     show      : ['bars','y-axis','x-axis','legend'],
     fill      : 3,
     fill      : 3,
+    linewidth : 3,
     timezone  : 'browser', // browser, utc or a standard timezone
     timezone  : 'browser', // browser, utc or a standard timezone
     spyable   : true,
     spyable   : true,
     zoomlinks : true,
     zoomlinks : true,
@@ -148,7 +149,6 @@ angular.module('kibana.histogram', [])
           i++;
           i++;
         });
         });
 
 
-        eventBus.broadcast($scope.$id,$scope.panel.group,'hits',$scope.hits)
         $scope.$emit('render')
         $scope.$emit('render')
         if(_segment < $scope.panel.index.length-1) {
         if(_segment < $scope.panel.index.length-1) {
           $scope.get_data(_segment+1,query_id)
           $scope.get_data(_segment+1,query_id)
@@ -184,7 +184,7 @@ angular.module('kibana.histogram', [])
   }
   }
 
 
 })
 })
-.directive('histogram', function(eventBus) {
+.directive('histogramChart', function(eventBus) {
   return {
   return {
     restrict: 'A',
     restrict: 'A',
     link: function(scope, elem, attrs, ctrl) {
     link: function(scope, elem, attrs, ctrl) {
@@ -236,7 +236,12 @@ angular.module('kibana.histogram', [])
               },
               },
               series: {
               series: {
                 stack:  show.stack,
                 stack:  show.stack,
-                lines:  { show: show.lines, fill: scope.panel.fill/10 },
+                lines:  { 
+                  show: show.lines, 
+                  fill: scope.panel.fill/10, 
+                  lineWidth: scope.panel.linewidth,
+                  steps: true
+                },
                 bars:   { show: show.bars,  fill: 1, barWidth: barwidth/1.8 },
                 bars:   { show: show.bars,  fill: 1, barWidth: barwidth/1.8 },
                 points: { show: show.points, fill: 1, fillColor: false},
                 points: { show: show.points, fill: 1, fillColor: false},
                 shadowSize: 1
                 shadowSize: 1
@@ -251,7 +256,8 @@ angular.module('kibana.histogram', [])
                 color: "#000",
                 color: "#000",
               },
               },
               selection: {
               selection: {
-                mode: "x"
+                mode: "x",
+                color: '#666'
               },
               },
               grid: {
               grid: {
                 backgroundColor: '#fff',
                 backgroundColor: '#fff',
@@ -294,23 +300,25 @@ angular.module('kibana.histogram', [])
         var tooltip = $('#pie-tooltip').length ? 
         var tooltip = $('#pie-tooltip').length ? 
           $('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
           $('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
         //var tooltip = $('#pie-tooltip')
         //var tooltip = $('#pie-tooltip')
-        tooltip.text(contents).css({
+        tooltip.html(contents).css({
           position: 'absolute',
           position: 'absolute',
           top     : y + 5,
           top     : y + 5,
           left    : x + 5,
           left    : x + 5,
-          color   : "#FFF",
-          border  : '1px solid #FFF',
-          padding : '2px',
-          'font-size': '8pt',
-          'background-color': '#000',
+          color   : "#000",
+          border  : '3px solid #000',
+          padding : '10px',
+          'font-size': '11pt',
+          'font-weight' : 'bold',
+          'background-color': '#FFF',
+          'border-radius': '10px',
         }).appendTo("body");
         }).appendTo("body");
       }
       }
 
 
       elem.bind("plothover", function (event, pos, item) {
       elem.bind("plothover", function (event, pos, item) {
         if (item) {
         if (item) {
-          var percent = parseFloat(item.series.percent).toFixed(1) + "%";
           tt(pos.pageX, pos.pageY,
           tt(pos.pageX, pos.pageY,
-            item.datapoint[1].toFixed(1) + " @ " + 
+            "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:20px;width:20px'></div> "+
+            item.datapoint[1].toFixed(0) + " @ " + 
             new Date(item.datapoint[0]).format('mm/dd HH:MM:ss'));
             new Date(item.datapoint[0]).format('mm/dd HH:MM:ss'));
         } else {
         } else {
           $("#pie-tooltip").remove();
           $("#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"> 
     <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 ['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>
-    <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>
-    <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>
   </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>
       </form>
     </div>
     </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>
+</div>

+ 12 - 1
panels/hits/module.html

@@ -1,3 +1,14 @@
 <kibana-panel ng-controller='hits' ng-init="init()">
 <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 data">
+        <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 data" style="float:left;padding-left: 10px;">
+     <span ng-show='panel.chart'><div style="display:inline-block;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.query.length > 1' hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
 </kibana-panel>         
 </kibana-panel>         

+ 175 - 25
panels/hits/module.js

@@ -5,58 +5,117 @@ angular.module('kibana.hits', [])
   var _d = {
   var _d = {
     query   : "*",
     query   : "*",
     group   : "default",
     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)
   _.defaults($scope.panel,_d)
 
 
   $scope.init = function () {
   $scope.init = function () {
     $scope.hits = 0;
     $scope.hits = 0;
     eventBus.register($scope,'time', function(event,time){
     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) {
     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
     // Now that we're all setup, request the time from our group
     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
     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
     // Make sure we have everything for the request to complete
     if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
     if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
       return
       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)
         ejs.RangeFilter($scope.time.field)
           .from($scope.time.from)
           .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
     // Populate scope when we have results
     results.then(function(results) {
     results.then(function(results) {
+
       $scope.panel.loading = false;
       $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;
         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) {
   function set_time(time) {
@@ -65,4 +124,95 @@ angular.module('kibana.hits', [])
     $scope.get_data();
     $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(){
+          console.log(scope.data)
+
+          // Populate element
+          try { 
+            var 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: ['#EB6841','#00A0B0','#6A4A3C','#EDC951','#CC333F']
+            })
+
+            var i = 0;
+            _.each(plot.getData(),function(series) {
+              scope.data[i].color = series.color;
+              i++;
+            })
+            
+            // 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  : '3px solid #000',
+          padding : '10px',
+          'font-size': '11pt',
+          'font-weight' : 'bold',
+          '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;display:inline-block;background:"+item.series.color+";height:20px;width:20px'></div> "+
+            item.datapoint[1].toFixed(0))
+        } else {
+          $("#pie-tooltip").remove();
+        }
+      });
+
+    }
+  };
 })
 })

+ 2 - 2
panels/stringquery/module.html

@@ -21,7 +21,7 @@
     </form>
     </form>
   </div>
   </div>
   <div class='row-fluid' ng-show="panel.multi && panel.multi_arrange == 'horizontal'">
   <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 ng-repeat="q in panel.query">
           <span class="input-append" style="margin-bottom:0px;margin-right:5px">
           <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>
             <button class="btn btn-danger" type="submit" style="width:50px;margin-left:-50px;visibility:hidden"></button>
@@ -31,7 +31,7 @@
         </span>
         </span>
       </form>
       </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 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>
   <div ng-show="panel.multi && panel.multi_arrange == 'vertical'">
   <div ng-show="panel.multi && panel.multi_arrange == 'vertical'">
     <form>
     <form>

+ 12 - 8
partials/dashboard.html

@@ -1,17 +1,21 @@
 
 
-<div class="row-fluid container">
+<div class="row-fluid container" style="margin-top:10px">
   <!-- Rows -->
   <!-- Rows -->
   <div ng-controller="dashcontrol" ng-init="init()"></div>
   <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">
+    <div class="row-control">
       <div class="row-fluid row-header" style="padding:0px;margin:0px;height:0px">
       <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>
       </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 -->
         <!-- 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">
         <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 -->
           <!-- Error Panel -->