Просмотр исходного кода

Moved panels to ng-include with controllers, collapsed histo and pies into single modules, added table module

Rashid Khan 13 лет назад
Родитель
Сommit
c890ca6ab1

+ 1 - 1
config.js

@@ -30,7 +30,7 @@ var config = new Settings(
     timefield:      '@timestamp', 
     //indexpattern:  '"logstash-"yyyy.mm.dd',
     indexpattern:   '"shakespeare"', 
-    modules:        ['histogram','pieterms','piequery','stackedquery','map'], 
+    modules:        ['histogram','map','pie','table'], 
 
     defaultfields:  ['line_text'],
     perpage:        50,

+ 42 - 38
dashboards.js

@@ -1,55 +1,59 @@
 var dashboards = 
 {
   title: "Infinite Monkey Dashboard",
-  rows: {
-    row1: {
-      height: "200px",
-      panels: {
-        "Monkey Productivity": {
+  rows: [
+    {
+      height: "300px",
+      panels: [
+        {
+          title   : "Monkey Shakespeare Lines",
           type    : "histogram",
-          span    : 8,
-          show    : ['lines','points'],
-          query   : "*",
-          label   : "Monkey lines of shakespeare",
+          span    : 6,
+          show    : ['lines','stack'],
+          fill    : 1,
+          query   : [
+            { label : "US", query : "country:US", color: '#86B32D' },
+            { label : "CN", query : "country:CN", color: '#BF3030' },
+            { label : "IN", query : "country:IN", color: '#1D7373' }
+          ],
           color   : "#7BA4AF"
         },
-        "Works of Shakespeare": {
-          type    : "pieterms",
-          legend  : true,
-          field   : "play_name",
-          span    : 4,
-          size    : 10,
+        {
+          title   : "World Monkeys",
+          type    : "map",
+          map     : 'world',
+          field   : "country",
+          span    : 6,
+          size    : 500,
           query   : "*"
         }
-      }
+      ]
     },
-    row2: {
+    {
       height: "300px",
-      panels: {
-        "Royal Decrees": {
-          type    : "stackedquery",
-          span    : 3,
+      panels: [
+        {
+          title   : "Hamlet vs Macbeth",
+          type    : "pie",
+          span    : 4,
+          size    : 8,
           donut   : true,
-          queries : ['king','queen','duke'],
-        },
-        "Remote Monkey Activity": {
-          type    : "map",
-          span    : 6,
-          size    : 20,
+          colors  : ['#BF3030','#1D7373','#86B32D','#A60000','#006363','#679B00'],
           field   : 'country',
-          query   : '',
-          colors  : ['#B07737','#85004B','#7BA4AF'],
+          //query   : { query: "*", field: "country"}
+          query   : [
+            { label : "Hamlet", query : "play_name:Hamlet", color: '#86B32D' },
+            { label : "Macbeth", query : "play_name:macbeth", color: '#BF3030' },
+          ]
         },
-        "Main Characters": {
-          type    : "pieterms",
-          donut   : true,
-          legend  : true,
-          field   : "country",
-          span    : 3,
-          size    : 5,
+        {
+          title   : "Newest Lines",
+          type    : "table",
+          span    : 8,
           query   : "*",
+          fields  : ['@timestamp','speaker','text_entry']
         }
-      }
+      ]
     }
-  }
+  ]
 };

+ 1 - 1
js/app.js

@@ -33,7 +33,7 @@ var labjs = $LAB
   .script("dashboards.js");
 
 _.each(config.modules, function(v) {
-  labjs = labjs.script('js/panels/'+v+'/module.js').wait()
+  labjs = labjs.script('panels/'+v+'/module.js').wait()
   modules.push('kibana.'+v)
 })
 

+ 0 - 2
js/controllers.js

@@ -35,8 +35,6 @@ angular.module('kibana.controllers', [])
   }
   var mytimeout = $timeout($scope.play,config.refresh);
 
-
-
   // If from/to to change, update index list
   $scope.$watch(function() { 
     return angular.toJson([$scope.from, $scope.to]) 

+ 0 - 150
js/panels/histogram/module.js

@@ -1,150 +0,0 @@
-labjs = labjs.script("common/lib/panels/jquery.flot.js")
-  .script("common/lib/panels/jquery.flot.time.js")
-
-angular.module('kibana.histogram', [])
-.directive('histogram', function() {
-  return {
-    restrict: 'A',
-    link: function(scope, elem, attrs) {
-
-      // Specify defaults for ALL directives
-      var _d = {
-        query   : "*",
-        interval: secondsToHms(calculate_interval(scope.from,scope.to,40,0)/1000),
-        color   : "#27508C",
-        show    : ['bars']
-      }
-
-      // Set ready flag and fill parameters (REQUIRED IN EVERY PANEL)
-      scope.$watch(function () {
-        return (attrs.params && scope.index) ? true : false;
-      }, function (ready) {
-        scope.ready = ready;
-        if(ready) {
-          scope.params = JSON.parse(attrs.params);
-          _.each(_d, function(v, k) {
-            scope.params[k] = _.isUndefined(scope.params[k]) 
-              ? _d[k] : scope.params[k];
-          });
-        }
-      });
-
-      // Also get the data if time frame changes.
-      // (REQUIRED IN EVERY PANEL)
-      scope.$watch(function() { 
-        return angular.toJson([scope.from, scope.to, scope.ready]) 
-      }, function(){
-        if(scope.ready)
-          if (_.isUndefined(attrs.params.interval))
-            scope.params.interval = secondsToHms(
-              calculate_interval(scope.from,scope.to,50,0)/1000),
-          get_data(scope,elem,attrs);
-      });
-
-      // Re-rending the panel if it is resized,
-      scope.$watch('data', function() {
-        if(scope.ready)
-          render_panel(scope,elem,attrs);
-      });
-
-      // Or if the model changes
-      angular.element(window).bind('resize', function(){
-          render_panel(scope,elem,attrs);
-      });
-
-      // Function for getting data
-      function get_data(scope,elem,attrs) {
-        var params = scope.params;
-        var ejs = scope.ejs;
-        var request = ejs.Request().indices(scope.index);
-        
-        // Build the question part of the query
-        var query = ejs.FilteredQuery(
-          ejs.QueryStringQuery(params.query || '*'),
-          ejs.RangeFilter(config.timefield)
-            .from(scope.from)
-            .to(scope.to)
-            .cache(false)
-          );
-
-        // Then the insert into facet and make the request
-        var results = request
-          .facet(ejs.DateHistogramFacet('histogram')
-            .field(config.timefield)
-            .interval(params.interval)
-            .facetFilter(ejs.QueryFilter(query))
-          )
-          .doSearch();
-
-        // Populate scope when we have results
-        results.then(function(results) {
-          scope.hits = results.hits.total;
-          scope.data = results.facets.histogram.entries;
-        });
-      }
-
-      // Function for rendering panel
-      function render_panel(scope,elem,attrs) {
-        // Parse our params object
-        var params = scope.params;
-
-        // Determine format
-        var show = _.isUndefined(params.show) ? {
-            bars: true, lines: false, points: false
-          } : {
-            lines:  _.indexOf(params.show,'lines') < 0 ? false : true,
-            bars:   _.indexOf(params.show,'bars') < 0 ? false : true,
-            points: _.indexOf(params.show,'points') < 0 ? false : true,
-          }
-
-        // Push null values at beginning and end of timeframe
-        scope.graph = [
-          [scope.from.getTime(), null],[scope.to.getTime(), null]];
-
-        // Create FLOT value array 
-        _.each(scope.data, function(v, k) {
-          scope.graph.push([v['time'],v['count']])
-        });
-
-        // Set barwidth based on specified interval
-        var barwidth = interval_to_seconds(params.interval)*1000
-
-        // Populate element
-        $.plot(elem, [{
-          label: _.isUndefined(params.label) ? params.query: params.label, 
-          data: scope.graph
-        }], {
-          legend: { 
-            position: "nw", 
-            labelFormatter: function(label, series) {
-              return '<span class="legend">' + label + ' / ' + params.interval 
-                + '</span>';
-            }
-          },
-          series: {
-            lines:  { show: show.lines, fill: false },
-            bars:   { show: show.bars,  fill: 1, barWidth: barwidth/1.8 },
-            points: { show: show.points },
-            color: params.color,
-            shadowSize: 1
-          },
-          yaxis: { min: 0, color: "#000" },
-          xaxis: {
-            mode: "time",
-            timeformat: "%H:%M:%S<br>%m-%d",
-            label: "Datetime",
-            color: "#000",
-          },
-          grid: {
-            backgroundColor: '#fff',
-            borderWidth: 0,
-            borderColor: '#eee',
-            color: "#eee",
-            hoverable: true,
-          }
-        });
-        //elem.show();
-      }
-    }
-  };
-})

+ 0 - 121
js/panels/map/module.js

@@ -1,121 +0,0 @@
-labjs = labjs.script("common/lib/panels/jquery.jvectormap.min.js");
-
-angular.module('kibana.map', [])
-.directive('map', function() {
-  return {
-    restrict: 'A',
-    link: function(scope, elem, attrs) {
-
-      // Specify defaults for ALL directives
-      var _d = {
-        queries : ["*"],
-        map     : "world",
-        interval: secondsToHms(calculate_interval(scope.from,scope.to,40,0)/1000),
-        colors  : ["#BF3030","#1D7373","#86B32D","#A98A21","#411F73"],
-        show    : ['bars'],
-        size    : 100,
-        exclude : []
-      }
-
-      // Set ready flag and fill parameters (REQUIRED IN EVERY PANEL)
-      scope.$watch(function () {
-        return (attrs.params && scope.index) ? true : false;
-      }, function (ready) {
-        scope.ready = ready;
-        if(ready) {
-          scope.params = JSON.parse(attrs.params);
-          _.each(_d, function(v, k) {
-            scope.params[k] = _.isUndefined(scope.params[k]) 
-              ? _d[k] : scope.params[k];
-          });
-        }
-      });
-
-      // Also get the data if time frame changes.
-      // (REQUIRED IN EVERY PANEL)
-      scope.$watch(function() { 
-        return angular.toJson([scope.from, scope.to, scope.ready]) 
-      }, function(){
-        if(scope.ready)
-          get_data(scope,elem,attrs);
-      });
-
-      // Re-rending panel if data changes
-      scope.$watch('data', function() {
-        if(scope.ready)
-          render_panel(scope,elem,attrs);
-      });
-
-      // Or if the window is resized
-      angular.element(window).bind('resize', function(){
-          render_panel(scope,elem,attrs);
-      });
-
-      // Function for getting data
-      function get_data(scope,elem,attrs) {
-        var params = scope.params;
-        var ejs = scope.ejs;
-        var request = ejs.Request().indices(scope.index);
-        
-        // Build the question part of the query
-        var query = ejs.FilteredQuery(
-          ejs.QueryStringQuery(params.query || '*'),
-          ejs.RangeFilter(config.timefield)
-            .from(scope.from)
-            .to(scope.to)
-            .cache(false)
-          );
-
-        // Then the insert into facet and make the request
-        var results = request
-          .facet(ejs.TermsFacet('map')
-            .field(params.field)
-            .size(params['size'])
-            .exclude(params.exclude)
-            .facetFilter(ejs.QueryFilter(query))
-          )
-          .doSearch();
-
-        // Populate scope when we have results
-        results.then(function(results) {
-          scope.hits = results.hits.total;
-          scope.data = {};
-          _.each(results.facets.map.terms, function(v) {
-            scope.data[v.term.toUpperCase()] = v.count;
-          });
-        });
-      }
-
-      // Function for rendering panel
-      function render_panel(scope,elem,attrs) {
-        // Parse our params object
-        var params = scope.params;
-
-        elem.text('');
-        $('.jvectormap-label,.jvectormap-zoomin,.jvectormap-zoomout').remove();
-
-        elem.append("<div class='jvectormap-label'>Loading Map</div>");
-
-        loadmap = $LAB.script("common/lib/panels/map."+params.map+".js")
-
-        // Populate element. Note that jvectormap appends, does not replace.
-        loadmap.wait(function(){
-          elem.vectorMap({  
-            map: params.map,
-            regionStyle: {initial: {fill: '#ddd'}},
-            zoomOnScroll: false,
-            backgroundColor: '#fff',
-            series: {
-              regions: [{
-                values: scope.data,
-                scale: ['#C8EEFF', '#0071A4'],
-                normalizeFunction: 'polynomial'
-              }]
-            }
-          });
-        })
-        //elem.show();
-      }
-    }
-  };
-});

+ 0 - 134
js/panels/piequery/module.js

@@ -1,134 +0,0 @@
-labjs = labjs.script("common/lib/panels/jquery.flot.js")
-  .script("common/lib/panels/jquery.flot.pie.js")
-
-angular.module('kibana.piequery', [])
-.directive('piequery', function() {
-  return {
-    restrict: 'A',
-    link: function(scope, elem, attrs) {
-
-      // Specify defaults for ALL directives
-      var _d = {
-        queries : ["*"],
-        donut   : false, 
-        tilt    : false,
-        legend  : true,
-      }
-
-      // Set ready flag and fill parameters (REQUIRED IN EVERY PANEL)
-      scope.$watch(function () {
-        return (attrs.params && scope.index) ? true : false;
-      }, function (ready) {
-        scope.ready = ready;
-        if(ready) {
-          scope.params = JSON.parse(attrs.params);
-          _.each(_d, function(v, k) {
-            scope.params[k] = _.isUndefined(scope.params[k]) 
-              ? _d[k] : scope.params[k];
-          });
-        }
-      });
-
-      // Also get the data if time frame changes.
-      // (REQUIRED IN EVERY PANEL)
-      scope.$watch(function() { 
-        return angular.toJson([scope.from, scope.to, scope.ready]) 
-      }, function(){
-        if(scope.ready)
-          get_data(scope,elem,attrs);
-      });
-
-      // Re-rending the panel if it is resized,
-      scope.$watch('data', function() {
-        if(scope.ready)
-          render_panel(scope,elem,attrs);
-      });
-
-      // Or if the model changes
-      angular.element(window).bind('resize', function(){
-          render_panel(scope,elem,attrs);
-      });
-
-      // Function for getting data
-      function get_data(scope,elem,attrs) {
-        var params = scope.params;
-        var ejs = scope.ejs;
-        var request = ejs.Request().indices(scope.index);
-        
-
-        var queries = [];
-        // Build the question part of the query
-        _.each(params.queries, function(v) {
-          queries.push(ejs.FilteredQuery(
-            ejs.QueryStringQuery(v || '*'),
-            ejs.RangeFilter(config.timefield)
-              .from(scope.from)
-              .to(scope.to)
-              .cache(false))
-          )
-        });
-
-        _.each(queries, function(v) {
-          request = request.facet(ejs.QueryFacet(_.indexOf(queries,v))
-            .query(v)
-            .facetFilter(ejs.QueryFilter(v))
-          )
-        })
-        // Then the insert into facet and make the request
-        var results = request.doSearch();
-
-        // Populate scope when we have results
-        results.then(function(results) {
-          scope.hits = results.hits.total;
-          scope.data = results.facets;
-        });
-      }
-
-      // Function for rendering panel
-      function render_panel(scope,elem,attrs) {
-        // Parse our params object
-        var params = scope.params;
-
-        // Create graph array
-        scope.graph = [];
-        _.each(scope.data, function(v, k) {
-          var point = {
-            label : params.queries[k],
-            data  : v['count']
-          }
-          if(!_.isUndefined(params.colors))
-            point.color = params.colors[k%params.colors.length];
-          scope.graph.push(point)
-        });
-
-        // Populate element
-        $.plot(elem, scope.graph, {
-            series: {
-              pie: {
-                innerRadius: params.donut ? 0.4 : 0,
-                tilt: params.tilt ? 0.45 : 1,
-                radius: 1,
-                show: true,
-                combine: {
-                  color: '#999',
-                  label: 'The Rest'
-                },
-                label: { 
-                  show: true,
-                  radius: 2/3,
-                  formatter: function(label, series){
-                    return '<div style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
-                      label+'<br/>'+Math.round(series.percent)+'%</div>';
-                  },
-                  threshold: 0.1 
-                }
-              }
-            },
-            //grid: { hoverable: true, clickable: true },
-            legend: { show: params.legend }
-          });
-        //elem.show();
-      }
-    }
-  };
-})

+ 0 - 140
js/panels/pieterms/module.js

@@ -1,140 +0,0 @@
-labjs = labjs.script("common/lib/panels/jquery.flot.js")
-  .script("common/lib/panels/jquery.flot.pie.js")
-
-angular.module('kibana.pieterms', [])
-.directive('pieterms', function() {
-  return {
-    restrict: 'A',
-    link: function(scope, elem, attrs) {
-
-      // Specify defaults for ALL directives
-      var _d = {
-        size    : 5,
-        query   : "*",
-        exclude : [],
-        donut   : false, 
-        tilt    : false,
-        legend  : true,
-      }
-
-      // Set ready flag and fill parameters (REQUIRED IN EVERY PANEL)
-      scope.$watch(function () {
-        return (attrs.params && scope.index) ? true : false;
-      }, function (ready) {
-        scope.ready = ready;
-        if(ready) {
-          scope.params = JSON.parse(attrs.params);
-          _.each(_d, function(v, k) {
-            scope.params[k] = _.isUndefined(scope.params[k]) 
-              ? _d[k] : scope.params[k];
-          });
-        }
-      });
-
-      // Also get the data if time frame changes.
-      // (REQUIRED IN EVERY PANEL)
-      scope.$watch(function() { 
-        return angular.toJson([scope.from, scope.to, scope.ready]) 
-      }, function(){
-        if(scope.ready)
-          get_data(scope,elem,attrs);
-      });
-
-      // Re-rending the panel if it is resized,
-      scope.$watch('data', function() {
-        if(scope.ready)
-          render_panel(scope,elem,attrs);
-      });
-
-      // Or if the model changes
-      angular.element(window).bind('resize', function(){
-          render_panel(scope,elem,attrs);
-      });
-
-      // Function for getting data
-      function get_data(scope,elem,attrs) {
-        var params = scope.params;
-        var ejs = scope.ejs;
-        var request = ejs.Request().indices(scope.index);
-        
-        // Build the question part of the query
-        var query = ejs.FilteredQuery(
-          ejs.QueryStringQuery(params.query || '*'),
-          ejs.RangeFilter(config.timefield)
-            .from(scope.from)
-            .to(scope.to)
-            .cache(false)
-          );
-
-        // Then the insert into facet and make the request
-        var results = request
-          .facet(ejs.TermsFacet('termpie')
-            .field(params.field)
-            .size(params['size'])
-            .exclude(params.exclude)
-            .facetFilter(ejs.QueryFilter(query))
-          )
-          .doSearch();
-
-        // Populate scope when we have results
-        results.then(function(results) {
-          scope.hits = results.hits.total;
-          scope.data = results.facets.termpie.terms;
-        });
-      }
-
-      // Function for rendering panel
-      function render_panel(scope,elem,attrs) {
-        // Parse our params object
-        var params = scope.params;
-
-        // Create graph array
-        scope.graph = [];
-        _.each(scope.data, function(v, k) {
-          if(!_.isUndefined(params.only) && _.indexOf(params.only,v['term']) < 0)
-            return
-
-          var point = {
-            label : v['term'],
-            data  : v['count']
-          }
-
-          if(!_.isUndefined(params.colors))
-            point.color = params.colors[_.indexOf(params.only,v['term'])] 
-
-          scope.graph.push(point)
-        });
-
-        var pie = {
-          series: {
-            pie: {
-              innerRadius: params.donut ? 0.4 : 0,
-              tilt: params.tilt ? 0.45 : 1,
-              radius: 1,
-              show: true,
-              combine: {
-                color: '#999',
-                label: 'The Rest'
-              },
-              label: { 
-                show: true,
-                radius: 2/3,
-                formatter: function(label, series){
-                  return '<div style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
-                    label+'<br/>'+Math.round(series.percent)+'%</div>';
-                },
-                threshold: 0.1 
-              }
-            }
-          },
-          //grid: { hoverable: true, clickable: true },
-          legend: { show: params.legend }
-        };
-
-        // Populate element
-        $.plot(elem, scope.graph, pie);
-        //elem.show();
-      }
-    }
-  };
-})

+ 0 - 165
js/panels/stackedquery/module.js

@@ -1,165 +0,0 @@
-labjs = labjs.script("common/lib/panels/jquery.flot.js")
-  .script("common/lib/panels/jquery.flot.time.js")
-  .script("common/lib/panels/jquery.flot.stack.js")
-
-angular.module('kibana.stackedquery', [])
-.directive('stackedquery', function() {
-  return {
-    restrict: 'A',
-    link: function(scope, elem, attrs) {
-
-      // Specify defaults for ALL directives
-      var _d = {
-        queries : ["*"],
-        interval: secondsToHms(calculate_interval(scope.from,scope.to,40,0)/1000),
-        colors  : ["#BF3030","#1D7373","#86B32D","#A98A21","#411F73"],
-        show    : ['bars']
-      }
-
-      // Set ready flag and fill parameters (REQUIRED IN EVERY PANEL)
-      scope.$watch(function () {
-        return (attrs.params && scope.index) ? true : false;
-      }, function (ready) {
-        scope.ready = ready;
-        if(ready) {
-          scope.params = JSON.parse(attrs.params);
-          _.each(_d, function(v, k) {
-            scope.params[k] = _.isUndefined(scope.params[k]) 
-              ? _d[k] : scope.params[k];
-          });
-        }
-      });
-
-      // Also get the data if time frame changes.
-      // (REQUIRED IN EVERY PANEL)
-      scope.$watch(function() { 
-        return angular.toJson([scope.from, scope.to, scope.ready]) 
-      }, function(){
-        if(scope.ready)
-          if (_.isUndefined(attrs.params.interval))
-            scope.params.interval = secondsToHms(
-              calculate_interval(scope.from,scope.to,50,0)/1000),
-          get_data(scope,elem,attrs);
-      });
-
-      // Re-rending the panel if it is resized,
-      scope.$watch('data', function() {
-        if(scope.ready)
-          render_panel(scope,elem,attrs);
-      });
-
-      // Or if the model changes
-      angular.element(window).bind('resize', function(){
-          render_panel(scope,elem,attrs);
-      });
-
-      // Function for getting data
-      function get_data(scope,elem,attrs) {
-        var params = scope.params;
-        var ejs = scope.ejs;
-        var request = ejs.Request().indices(scope.index);
-        
-        // Build the question part of the query
-        var queries = [];
-        _.each(params.queries, function(v) {
-          queries.push(ejs.FilteredQuery(
-            ejs.QueryStringQuery(v || '*'),
-            ejs.RangeFilter(config.timefield)
-              .from(scope.from)
-              .to(scope.to)
-              .cache(false))
-          )
-        });
-
-        // Build the facet part
-        _.each(queries, function(v) {
-          request = request
-            .facet(ejs.DateHistogramFacet(_.indexOf(queries,v))
-              .field(config.timefield)
-              .interval(params.interval)
-              .facetFilter(ejs.QueryFilter(v))
-            )
-        })
-
-        // Then run it
-        var results = request.doSearch();
-
-        // Populate scope when we have results
-        results.then(function(results) {
-          scope.hits = results.hits.total;
-          scope.data = results.facets;
-        });
-      }
-
-      // Function for rendering panel
-      function render_panel(scope,elem,attrs) {
-        // Parse our params object
-        var params = scope.params;
-
-        // Determine format
-        var show = _.isUndefined(params.show) ? {
-            bars: true, lines: false, points: false, fill: false
-          } : {
-            lines:  _.indexOf(params.show,'lines') < 0 ? false : true,
-            bars:   _.indexOf(params.show,'bars') < 0 ? false : true,
-            points: _.indexOf(params.show,'points') < 0 ? false : true,
-            fill:   _.indexOf(params.show,'fill') < 0 ? false : true
-          }
-
-        scope.graph = [];
-        // Push null values at beginning and end of timeframe
-        _.each(scope.data, function(v, k) {
-          var series = {};
-          var data = [[scope.from.getTime(), null]];
-          _.each(v.entries, function(v, k) {
-            data.push([v['time'],v['count']])
-          });
-          data.push([scope.to.getTime(), null])
-          series.data = {
-            label: params.queries[k], 
-            data: data, 
-            color: params.colors[k%params.colors.length]
-          };
-          scope.graph.push(series.data)
-        });
-
-        // Set barwidth based on specified interval
-        var barwidth = interval_to_seconds(params.interval)*1000
-
-        // Populate element
-        $.plot(elem, scope.graph, {
-          legend: { 
-            position: "nw", 
-            labelFormatter: function(label, series) {
-              return '<span class="legend">' + label + ' / ' + params.interval 
-                + '</span>';
-            }
-          },
-          series: {
-            stack:  0,
-            lines:  { show: show.lines, fill: show.fill },
-            bars:   { show: show.bars,  fill: 1, barWidth: barwidth/1.8 },
-            points: { show: show.points },
-            color: params.color,
-            shadowSize: 1
-          },
-          yaxis: { min: 0, color: "#000" },
-          xaxis: {
-            mode: "time",
-            timeformat: "%H:%M:%S<br>%m-%d",
-            label: "Datetime",
-            color: "#000",
-          },
-          grid: {
-            backgroundColor: '#fff',
-            borderWidth: 0,
-            borderColor: '#eee',
-            color: "#eee",
-            hoverable: true,
-          }
-        });
-        //elem.show();
-      }
-    }
-  };
-})

+ 4 - 0
panels/histogram/module.html

@@ -0,0 +1,4 @@
+<div ng-controller='histogram'>
+  <h4>{{panel.title}}</h4>
+  <div histogram params="{{panel}}" style="height:{{row.height}}"></div>
+</div>         

+ 149 - 0
panels/histogram/module.js

@@ -0,0 +1,149 @@
+angular.module('kibana.histogram', [])
+.controller('histogram', function($scope, $location) {
+
+  // Set and populate defaults
+  var _d = {
+    query   : "*",
+    interval: secondsToHms(calculate_interval($scope.from,$scope.to,40,0)/1000),
+    color   : "#27508C",
+    show    : ['bars'],
+    fill    : false,
+  }
+  _.each(_d, function(v, k) {
+    $scope.panel[k] = _.isUndefined($scope.panel[k]) 
+      ? _d[k] : $scope.panel[k];
+  });
+
+  $scope.get_data = function() {
+    var request = $scope.ejs.Request().indices($scope.index);
+    
+    // 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(config.timefield)
+          .from($scope.from)
+          .to($scope.to)
+          .cache(false))
+      )
+    });
+
+    // Build the facet part
+    _.each(queries, function(v) {
+      request = request
+        .facet($scope.ejs.DateHistogramFacet(_.indexOf(queries,v))
+          .field(config.timefield)
+          .interval($scope.panel.interval)
+          .facetFilter($scope.ejs.QueryFilter(v))
+        ).size(0)
+    })
+
+    // Then run it
+    var results = request.doSearch();
+
+    // Populate scope when we have results
+    results.then(function(results) {
+      $scope.hits = results.hits.total;
+      // Null values at each end of the time range make sure we see entire range
+
+      $scope.data = [];
+      _.each(results.facets, function(v, k) {
+        var series = {};
+        var data = [[$scope.from.getTime(), null]];
+        _.each(v.entries, function(v, k) {
+          data.push([v['time'],v['count']])
+        });
+        data.push([$scope.to.getTime(), null])
+        series.data = {
+          label: $scope.panel.query[k].label, 
+          data: data, 
+        };
+        if (!(_.isUndefined($scope.panel.query[k].color)))
+          series.data.color = $scope.panel.query[k].color;
+        $scope.data.push(series.data)
+      });
+    });
+  }
+
+  $scope.$watch(function() { 
+    return angular.toJson([$scope.from, $scope.to]) 
+  }, function(){
+    $scope.panel.interval = secondsToHms(
+      calculate_interval($scope.from,$scope.to,50,0)/1000),
+    $scope.get_data();
+  });
+
+})
+.directive('histogram', function() {
+  return {
+    restrict: 'A',
+    link: function(scope, elem, attrs, ctrl) {
+
+      scope.$watch('data', function() {
+        if(!(_.isUndefined(scope.data)))
+          render_panel(scope,elem,attrs);
+      });
+
+      // Re-render if the window is
+      angular.element(window).bind('resize', function(){
+          render_panel(scope,elem,attrs);
+      });
+
+      // Function for rendering panel
+      function render_panel(scope,elem,attrs) {
+        // 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,
+          }
+
+        // Set barwidth based on specified interval
+        var barwidth = interval_to_seconds(scope.panel.interval)*1000
+
+        var scripts = $LAB.script("common/lib/panels/jquery.flot.js")
+          .script("common/lib/panels/jquery.flot.time.js")
+          .script("common/lib/panels/jquery.flot.stack.js")
+                    
+        // Populate element. Note that jvectormap appends, does not replace.
+        scripts.wait(function(){
+        // Populate element
+          $.plot(elem, scope.data, {
+            legend: { 
+              position: "nw", 
+              labelFormatter: function(label, series) {
+                return '<span class="legend">' + label + ' / ' + 
+                  scope.panel.interval + '</span>';
+              }
+            },
+            series: {
+              stack:  show.stack,
+              lines:  { show: show.lines, fill: scope.panel.fill },
+              bars:   { show: show.bars,  fill: 1, barWidth: barwidth/1.8 },
+              points: { show: show.points },
+              shadowSize: 1
+            },
+            yaxis: { min: 0, color: "#000" },
+            xaxis: {
+              mode: "time",
+              timeformat: "%H:%M:%S<br>%m-%d",
+              label: "Datetime",
+              color: "#000",
+            },
+            grid: {
+              backgroundColor: '#fff',
+              borderWidth: 0,
+              borderColor: '#eee',
+              color: "#eee",
+              hoverable: true,
+            }
+          })
+        })
+      }
+    }
+  };
+})

+ 4 - 0
panels/map/module.html

@@ -0,0 +1,4 @@
+<div ng-controller='map'>
+  <h4>{{panel.title}}</h4>
+  <div map params="{{panel}}" style="height:{{row.height}}"></div>
+</div>

+ 111 - 0
panels/map/module.js

@@ -0,0 +1,111 @@
+angular.module('kibana.map', [])
+.controller('map', function($scope, $location) {
+
+  // Set and populate defaults
+  var _d = {
+    query   : "*",
+    map     : "world",
+    colors  : ['#C8EEFF', '#0071A4'],
+    size    : 100,
+    exclude : []
+  }
+  _.each(_d, function(v, k) {
+    $scope.panel[k] = _.isUndefined($scope.panel[k]) 
+      ? _d[k] : $scope.panel[k];
+  });
+
+  $scope.get_data = function() {
+    var request = $scope.ejs.Request().indices($scope.index);
+
+    // Then the insert into facet and make the request
+    var results = request
+      .facet(ejs.TermsFacet('map')
+        .field($scope.panel.field)
+        .size($scope.panel['size'])
+        .exclude($scope.panel.exclude)
+        .facetFilter(ejs.QueryFilter(
+          ejs.FilteredQuery(
+            ejs.QueryStringQuery($scope.panel.query || '*'),
+            ejs.RangeFilter(config.timefield)
+              .from($scope.from)
+              .to($scope.to)
+              .cache(false)
+            )))).size(0)
+      .doSearch();
+
+    // Populate scope when we have results
+    results.then(function(results) {
+      $scope.hits = results.hits.total;
+      $scope.data = {};
+      _.each(results.facets.map.terms, function(v) {
+        $scope.data[v.term.toUpperCase()] = v.count;
+      });
+    });
+  }
+
+  $scope.$watch(function() { 
+    return angular.toJson([$scope.from, $scope.to]) 
+  }, function(){
+    $scope.get_data();
+  });
+
+})
+.directive('map', function() {
+  return {
+    restrict: 'A',
+    link: function(scope, elem, attrs) {
+
+      // Re-rending panel if data changes
+      scope.$watch('data', function() {
+        render_panel(scope,elem,attrs);
+      });
+
+      // Or if the window is resized
+      angular.element(window).bind('resize', function(){
+          render_panel(scope,elem,attrs);
+      });
+
+      function render_panel(scope,elem,attrs) {
+
+        // Using LABjs, wait until all scripts are loaded before rendering panel
+        var scripts = $LAB.script("common/lib/panels/jquery.jvectormap.min.js")
+          .script("common/lib/panels/map."+scope.panel.map+".js")
+                    
+        // Populate element. Note that jvectormap appends, does not replace.
+        scripts.wait(function(){
+          elem.text('');
+          $('.jvectormap-zoomin,.jvectormap-zoomout,.jvectormap-label').remove();
+          var map = elem.vectorMap({  
+            map: scope.panel.map,
+            regionStyle: {initial: {fill: '#ddd'}},
+            zoomOnScroll: false,
+            backgroundColor: '#fff',
+            series: {
+              regions: [{
+                values: scope.data,
+                scale: scope.panel.colors,
+                normalizeFunction: 'polynomial'
+              }]
+            },
+            onRegionLabelShow: function(event, label, code){
+              $('.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"
+              })
+              var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
+              $('.jvectormap-label').text(label.text() + ": " + count);
+            },
+            onRegionOut: function(event, code) {
+            }
+          });
+        })
+      }
+    }
+  };
+});

+ 4 - 0
panels/pie/module.html

@@ -0,0 +1,4 @@
+<div ng-controller='pie'>
+  <h4>{{panel.title}}</h4>
+  <div pie params="{{panel}}" style="height:{{row.height}}"></div>
+</div>         

+ 153 - 0
panels/pie/module.js

@@ -0,0 +1,153 @@
+labjs = labjs.script("common/lib/panels/jquery.flot.js")
+  .script("common/lib/panels/jquery.flot.pie.js")
+
+angular.module('kibana.pie', [])
+.controller('pie', function($scope, $location) {
+
+  // Set and populate defaults
+  var _d = {
+    query   : "*",
+    size    : 100,
+    exclude : [],
+    donut   : false,
+    tilt    : false,
+    legend  : true,
+  }
+  _.each(_d, function(v, k) {
+    $scope.panel[k] = _.isUndefined($scope.panel[k]) 
+      ? _d[k] : $scope.panel[k];
+  });
+
+  $scope.get_data = function() {
+    var request = $scope.ejs.Request().indices($scope.index);
+
+    // If we have an array, use query facet
+    if(_.isArray($scope.panel.query)) {
+      var queries = [];
+      // Build the question part of the query
+      _.each($scope.panel.query, function(v) {
+        queries.push(ejs.FilteredQuery(
+          ejs.QueryStringQuery(v.query || '*'),
+          ejs.RangeFilter(config.timefield)
+            .from($scope.from)
+            .to($scope.to)
+            .cache(false))
+        )
+      });
+
+      // Then the insert into facet and make the request
+      _.each(queries, function(v) {
+        request = request.facet(ejs.QueryFacet(_.indexOf(queries,v))
+          .query(v)
+          .facetFilter(ejs.QueryFilter(v))
+        )
+      })
+      var results = request.doSearch();
+
+      // Populate scope when we have results
+      results.then(function(results) {
+        $scope.hits = results.hits.total;
+        $scope.data = [];
+        _.each(results.facets, function(v, k) {
+          var series = {};
+          var slice = { label : $scope.panel.query[k].label, data : v.count }; 
+          if (!(_.isUndefined($scope.panel.query[k].color)))
+            slice.color = $scope.panel.query[k].color;
+          $scope.data.push(slice)
+        });
+      });
+    // If we don't have an array, assume its a term facet.
+    } else {
+      var results = request
+        .facet(ejs.TermsFacet('pie')
+          .field($scope.panel.query.field)
+          .size($scope.panel['size'])
+          .exclude($scope.panel.exclude)
+          .facetFilter(ejs.QueryFilter(
+            ejs.FilteredQuery(
+              ejs.QueryStringQuery($scope.panel.query.query || '*'),
+              ejs.RangeFilter(config.timefield)
+                .from($scope.from)
+                .to($scope.to)
+                .cache(false)
+              )))).size(0)
+        .doSearch();
+
+      // Populate scope when we have results
+      results.then(function(results) {
+        $scope.hits = results.hits.total;
+        $scope.data = [];
+        var k = 0;
+        _.each(results.facets.pie.terms, function(v) {
+          var slice = { label : v.term, data : v.count }; 
+          $scope.data.push();
+          if(!(_.isUndefined($scope.panel.colors)) 
+            && _.isArray($scope.panel.colors)
+            && $scope.panel.colors.length > 0) {
+            slice.color = $scope.panel.colors[k%$scope.panel.colors.length];
+          } 
+          $scope.data.push(slice)
+          k = k + 1;
+        });
+      });
+    }
+  }
+
+  $scope.$watch(function() { 
+    return angular.toJson([$scope.from, $scope.to]) 
+  }, function(){
+    $scope.get_data();
+  });
+
+})
+.directive('pie', function() {
+  return {
+    restrict: 'A',
+    link: function(scope, elem, attrs) {
+
+      // Re-rending the panel if it is resized,
+      scope.$watch('data', function() {
+        if(!(_.isUndefined(scope.data)))
+          render_panel(scope,elem,attrs);
+      });
+
+      // Or if the model changes
+      angular.element(window).bind('resize', function(){
+          render_panel(scope,elem,attrs);
+      });
+
+      // Function for rendering panel
+      function render_panel(scope,elem,attrs) {
+        var pie = {
+          series: {
+            pie: {
+              innerRadius: scope.panel.donut ? 0.4 : 0,
+              tilt: scope.panel.tilt ? 0.45 : 1,
+              radius: 1,
+              show: true,
+              combine: {
+                color: '#999',
+                label: 'The Rest'
+              },
+              label: { 
+                show: true,
+                radius: 2/3,
+                formatter: function(label, series){
+                  return '<div style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+                    label+'<br/>'+Math.round(series.percent)+'%</div>';
+                },
+                threshold: 0.1 
+              }
+            }
+          },
+          //grid: { hoverable: true, clickable: true },
+          legend: { show: scope.panel.legend }
+        };
+
+        // Populate element
+        $.plot(elem, scope.data, pie);
+        //elem.show();
+      }
+    }
+  };
+})

+ 13 - 0
panels/table/module.html

@@ -0,0 +1,13 @@
+<div ng-controller='table'>
+  <h4>{{panel.title}}</h4>
+  <div style="height:{{row.height}};overflow-y:auto;overflow-x:hidden"> 
+    <table class="table table-condensed table-striped">
+      <thead>
+        <th ng-repeat="field in panel.fields">{{field}}</th>
+      </thead>
+      <tr ng-repeat="row in data">
+        <td ng-repeat="field in panel.fields">{{row['_source'][field]}}</td>
+      </tr>
+    </table>
+  </div>
+</div>         

+ 58 - 0
panels/table/module.js

@@ -0,0 +1,58 @@
+angular.module('kibana.table', [])
+.controller('table', function($scope, $location) {
+
+  // Set and populate defaults
+  var _d = {
+    query   : "*",
+    size    : 100,
+    sort    : [config.timefield,'desc'],
+  }
+  _.each(_d, function(v, k) {
+    $scope.panel[k] = _.isUndefined($scope.panel[k]) 
+      ? _d[k] : $scope.panel[k];
+  });
+
+  $scope.get_data = function() {
+    var request = $scope.ejs.Request().indices($scope.index);
+
+    var results = request
+      .query(ejs.FilteredQuery(
+        ejs.QueryStringQuery($scope.panel.query || '*'),
+        ejs.RangeFilter(config.timefield)
+          .from($scope.from)
+          .to($scope.to)
+          .cache(false)
+        )
+      )
+      .size($scope.panel.size)
+      .sort($scope.panel.sort[0],$scope.panel.sort[1])
+      .doSearch();
+
+    // Populate scope when we have results
+    results.then(function(results) {
+      console.log(results)
+      $scope.hits = results.hits.total;
+      $scope.data = results.hits.hits;
+      /*
+      _.each(results.facets.pie.terms, function(v) {
+        var slice = { label : v.term, data : v.count }; 
+        $scope.data.push();
+        if(!(_.isUndefined($scope.panel.colors)) 
+          && _.isArray($scope.panel.colors)
+          && $scope.panel.colors.length > 0) {
+          slice.color = $scope.panel.colors[k%$scope.panel.colors.length];
+        } 
+        $scope.data.push(slice)
+        k = k + 1;
+      });
+*/
+    });
+  }
+
+  $scope.$watch(function() { 
+    return angular.toJson([$scope.from, $scope.to]) 
+  }, function(){
+    $scope.get_data();
+  });
+
+})

+ 2 - 4
partials/dashboard.html

@@ -5,10 +5,8 @@
     <div class="span4"><div><input type="file" id="upload" upload /></div></div>
   </div>
   <div class="row-fluid" ng-repeat="(row_name, row) in dashboards.rows" style="height:{{row.height}}">
-    <div ng-repeat="(panel_name, panel) in row.panels">
-      <div class="span{{panel.span}}" style="padding: 10px;height={{row.height}}">
-        <h4>{{panel_name}}</h4>
-        <div panel="{{panel.type}}"></div>
+    <div ng-repeat="(name, panel) in row.panels">
+      <div class="span{{panel.span}}" style="padding: 10px;height={{row.height}}" ng-include="'panels/'+panel.type+'/module.html'">
       </div>
     </div>
   </div>