Torkel Odegaard 12 лет назад
Родитель
Сommit
869bebed6e

+ 1 - 0
src/app/controllers/all.js

@@ -2,6 +2,7 @@ define([
   './dash',
   './dashLoader',
   './row',
+  './submenuCtrl',
   './pulldown',
   './search',
   './metricKeys',

+ 30 - 0
src/app/controllers/submenuCtrl.js

@@ -0,0 +1,30 @@
+define([
+  'angular',
+  'app',
+  'underscore'
+],
+function (angular, app, _) {
+  'use strict';
+
+  var module = angular.module('kibana.controllers');
+
+  module.controller('SubmenuCtrl', function($scope) {
+      var _d = {
+        collapse: false,
+        notice: false,
+        enable: true
+      };
+
+      _.defaults($scope.pulldown,_d);
+
+      $scope.init = function() {
+        $scope.panel = $scope.pulldown;
+        $scope.row = $scope.pulldown;
+      };
+
+      $scope.init();
+
+    }
+  );
+
+});

+ 2 - 2
src/app/directives/grafanaGraph.js

@@ -206,13 +206,13 @@ function (angular, $, kbn, moment, _) {
         }
 
         function addAnnotations(options) {
-          if(!scope.panel.annotate.enable) {
+          if(!data.annotations || data.annotations.length === 0) {
             return;
           }
 
           options.events = {
             levels: 1,
-            data: scope.annotations,
+            data: data.annotations,
             types: {
               'annotation': {
                 level: 1,

+ 8 - 3
src/app/panels/annotations/module.html

@@ -1,8 +1,13 @@
 <div ng-controller='AnnotationsCtrl' ng-init="init()">
 
-  <div class="submenu-toggle">
-    <label class="small" for="showAnnotations">Hide</label>
-    <input type="checkbox" id="showAnnotations"></label>
+ <!--  <div class="submenu-toggle" ng-class="{'annotation-disabled': panel.hideAll }">
+    <a ng-click="hideAll();" class="small">Hide All</a>
+    <i class="icon-ok"></i>
+  </div>
+ -->
+  <div class="submenu-toggle" ng-repeat="annotation in annotationList" ng-class="{'annotation-disabled': !annotation.enabled }">
+    <a ng-click="hide(annotation)" class="small">{{annotation.name}}</a>
+    <i class="icon-ok"></i>
   </div>
 
 </div>

+ 18 - 11
src/app/panels/annotations/module.js

@@ -14,7 +14,7 @@ function (angular, app, _) {
   var module = angular.module('kibana.panels.annotations', []);
   app.useModule(module);
 
-  module.controller('AnnotationsCtrl', function($scope) {
+  module.controller('AnnotationsCtrl', function($scope, dashboard, annotationsSrv, $rootScope) {
 
     $scope.panelMeta = {
       status  : "Stable",
@@ -24,19 +24,26 @@ function (angular, app, _) {
     // Set and populate defaults
     var _d = {
     };
+
     _.defaults($scope.panel,_d);
 
     $scope.init = function() {
-      $scope.panel.annotations = [
-        {
-          type: 'graphite-target',
-          target: 'metric'
-        },
-        {
-          type: 'graphite-target',
-          target: 'metric2'
-        }
-      ];
+      $scope.annotationList = annotationsSrv.annotationList;
+    };
+
+    $scope.hideAll = function () {
+      $scope.panel.hideAll = !$scope.panel.hideAll;
+
+      _.each($scope.annotationList, function(annotation) {
+        annotation.enabled = !$scope.panel.hideAll;
+      });
+    };
+
+    $scope.hide = function (annotation) {
+      annotation.enabled = !annotation.enabled;
+      $scope.panel.hideAll = !annotation.enabled;
+
+      $rootScope.$broadcast('refresh');
     };
 
 

+ 14 - 4
src/app/panels/graphite/module.js

@@ -34,7 +34,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
   var module = angular.module('kibana.panels.graphite', []);
   app.useModule(module);
 
-  module.controller('graphite', function($scope, $rootScope, filterSrv, graphiteSrv, $timeout) {
+  module.controller('graphite', function($scope, $rootScope, filterSrv, graphiteSrv, $timeout, annotationsSrv) {
 
     $scope.panelMeta = {
       modals : [],
@@ -243,6 +243,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
 
     $scope.updateTimeRange = function () {
       $scope.range = filterSrv.timeRange();
+      $scope.rangeUnparsed = filterSrv.timeRange(false);
+
       $scope.interval = '10m';
 
       if ($scope.range) {
@@ -279,12 +281,14 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
       $scope.updateTimeRange();
 
       var graphiteQuery = {
-        range: filterSrv.timeRange(false),
+        range: $scope.rangeUnparsed,
         targets: $scope.panel.targets,
-        renderer: $scope.panel.renderer,
+        format: $scope.panel.renderer === 'png' ? 'png' : 'json',
         maxDataPoints: $scope.panel.span * 50
       };
 
+      $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed);
+
       return graphiteSrv.query(graphiteQuery)
         .then($scope.receiveGraphiteData)
         .then(null, function(err) {
@@ -327,7 +331,13 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
         data.push(series);
       });
 
-      $scope.render(data);
+      $scope.annotationsPromise
+        .then(function(annotations) {
+          data.annotations = annotations;
+          $scope.render(data);
+        }, function() {
+          $scope.render(data);
+        });
     };
 
     $scope.add_target = function() {

+ 1 - 13
src/app/partials/dashboard.html

@@ -1,17 +1,5 @@
-<!-- is there a better way to repeat without actually affecting the page? -->
-<!-- <div class="filter-pulldown" ng-controller="PulldownCtrl" ng-show="pulldown.enable">
-  <div class="top-row-close pointer pull-left" ng-click="toggle_pulldown(pulldown);dismiss();" bs-tooltip="'Toggle '+pulldown.type" data-placement="bottom">
-    <span class="small"><strong>{{pulldown.type}}</strong></span>
-  </div>
-  <div class="top-row-open" ng-hide="pulldown.collapse">
-    <kibana-simple-panel type="filtering" ng-cloak></kibana-simple-panel>
-    <kibana-simple-panel type="annotations" ng-cloak></kibana-simple-panel>
-  </div>
-  <div class="clearfix"></div>
-</div> -->
-
 <div class="submenu-controls">
-  <div class="submenu-panel" ng-repeat="pulldown in dashboard.current.pulldowns | filter:{ enable: true }">
+  <div class="submenu-panel" ng-controller="SubmenuCtrl" ng-repeat="pulldown in dashboard.current.pulldowns | filter:{ enable: true }">
     <div class="submenu-panel-title">
       <span class="small"><strong>{{pulldown.type}}:</strong></span>
     </div>

+ 1 - 1
src/app/partials/dasheditor.html

@@ -133,7 +133,7 @@
     <ng-include src="'app/partials/import.html'"></ng-include>
   </div>
 
-  <div ng-repeat="pulldown in dashboard.current.nav" ng-controller="PulldownCtrl" ng-show="editor.index == 5+$index">
+  <div ng-repeat="pulldown in dashboard.current.nav" ng-controller="SubmenuCtrl" ng-show="editor.index == 5+$index">
     <ng-include ng-show="pulldown.enable" src="edit_path(pulldown.type)"></ng-include>
     <button ng-hide="pulldown.enable" class="btn" ng-click="pulldown.enable = true">Enable the {{pulldown.type}}</button>
   </div>

+ 1 - 0
src/app/services/all.js

@@ -6,5 +6,6 @@ define([
   './panelMove',
   './graphite/graphiteSrv',
   './keyboardManager',
+  './annotationsSrv',
 ],
 function () {});

+ 77 - 0
src/app/services/annotationsSrv.js

@@ -0,0 +1,77 @@
+define([
+  'angular',
+  'underscore',
+  'moment'
+], function (angular, _, moment) {
+  'use strict';
+
+  var module = angular.module('kibana.services');
+
+  module.service('annotationsSrv', function(dashboard, graphiteSrv, $q, alertSrv) {
+
+    this.init = function() {
+      this.annotationList = [
+        {
+          type: 'graphite-target',
+          enabled: false,
+          target: 'metrics_data.mysite.dolph.counters.payment.cart_klarna_payment_completed.count',
+          name: 'deploys',
+        },
+        {
+          type: 'graphite-target',
+          enabled: true,
+          target: 'metrics_data.mysite.dolph.counters.payment.cart_paypal_payment_completed.count',
+          name: 'restarts',
+        }
+      ];
+    };
+
+    this.getAnnotations = function(rangeUnparsed) {
+      var graphiteAnnotations = _.where(this.annotationList, { type: 'graphite-target', enabled: true });
+      var graphiteTargets = _.map(graphiteAnnotations, function(annotation) {
+        return { target: annotation.target };
+      });
+
+      if (graphiteTargets.length === 0) {
+        return $q.when(null);
+      }
+
+      var graphiteQuery = {
+        range: rangeUnparsed,
+        targets: graphiteTargets,
+        format: 'json',
+        maxDataPoints: 100
+      };
+
+      return graphiteSrv.query(graphiteQuery)
+        .then(function(results) {
+          return _.reduce(results.data, function(list, target) {
+            _.each(target.datapoints, function (values) {
+              if (values[0] === null) {
+                return;
+              }
+
+              list.push({
+                min: values[1] * 1000,
+                max: values[1] * 1000,
+                eventType: "annotation",
+                title: null,
+                description: "<small><i class='icon-tag icon-flip-vertical'></i>test</small><br>"+
+                  moment(values[1] * 1000).format('YYYY-MM-DD HH:mm:ss'),
+                score: 1
+              });
+            });
+
+            return list;
+          }, []);
+        })
+        .then(null, function() {
+          alertSrv.set('Annotations','Could not fetch annotations','error');
+        });
+    };
+
+    // Now init
+    this.init();
+  });
+
+});

+ 3 - 3
src/app/services/graphite/graphiteSrv.js

@@ -20,13 +20,13 @@ function (angular, _, $, config, kbn, moment) {
           from: this.translateTime(options.range.from),
           until: this.translateTime(options.range.to),
           targets: options.targets,
-          renderer: options.renderer,
+          format: options.format,
           maxDataPoints: options.maxDataPoints
         };
 
         var params = buildGraphiteParams(graphOptions);
 
-        if (options.renderer === 'png') {
+        if (options.format === 'png') {
           return $q.when(graphiteRenderUrl + '?' + params.join('&'));
         }
 
@@ -132,7 +132,7 @@ function (angular, _, $, config, kbn, moment) {
       var clean_options = [];
       var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
 
-      if (options.renderer !== 'png') {
+      if (options.format !== 'png') {
         options['format'] = 'json';
       }
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/css/bootstrap.dark.min.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/css/bootstrap.light.min.css


+ 0 - 0
src/vendor/bootstrap/less/grafana-responsive.less → src/css/less/grafana-responsive.less


+ 1 - 33
src/vendor/bootstrap/less/grafana.less → src/css/less/grafana.less

@@ -1,3 +1,4 @@
+@import "submenu.less";
 
 .navbar-static-top {
   border-bottom: 1px solid black;
@@ -37,39 +38,6 @@
   }
 }
 
-.submenu-controls {
-  background: #292929;
-  font-size: inherit;
-  label {
-    margin: 0;
-    padding-right: 4px;
-    display: inline;
-  }
-  input[type=checkbox] {
-    margin: 0;
-  }
-}
-
-.submenu-panel {
-  padding: 0 10px 0 17px;
-  border-right: 1px solid #202020;
-  float: left;
-}
-
-.submenu-panel-title {
-  float: left;
-  text-transform: uppercase;
-  padding: 3px 10px 0 0;
-}
-
-.submenu-panel-wrapper {
-  float: left;
-}
-
-.submenu-toggle {
-  padding: 3px 0;
-}
-
 .row-button {
   width: 24px;
 }

+ 0 - 0
src/vendor/bootstrap/less/overrides.less → src/css/less/overrides.less


+ 41 - 0
src/css/less/submenu.less

@@ -0,0 +1,41 @@
+.submenu-controls {
+  background: #292929;
+  font-size: inherit;
+  label {
+    margin: 0;
+    padding-right: 4px;
+    display: inline;
+  }
+  input[type=checkbox] {
+    margin: 0;
+  }
+}
+
+.submenu-panel {
+  padding: 0 10px 0 17px;
+  border-right: 1px solid #202020;
+  float: left;
+}
+
+.submenu-panel-title {
+  float: left;
+  text-transform: uppercase;
+  padding: 4px 10px 0 0;
+}
+
+.submenu-panel-wrapper {
+  float: left;
+}
+
+.submenu-toggle {
+  padding: 4px 0 0 4px;
+  float: left;
+}
+
+.submenu-toggle:first-child {
+  padding-left: 0;
+}
+
+.annotation-disabled, .annotation-disabled a {
+  color: darken(@textColor, 25%);
+}

+ 0 - 0
src/vendor/bootstrap/less/variables.dark.less → src/css/less/variables.dark.less


+ 0 - 0
src/vendor/bootstrap/less/variables.light.less → src/css/less/variables.light.less


+ 225 - 0
src/vendor/jquery/jquery.flot.fillbetween.js

@@ -0,0 +1,225 @@
+/* Flot plugin for computing bottoms for filled line and bar charts.
+
+Copyright (c) 2007-2013 IOLA and Ole Laursen.
+Licensed under the MIT license.
+
+The case: you've got two series that you want to fill the area between. In Flot
+terms, you need to use one as the fill bottom of the other. You can specify the
+bottom of each data point as the third coordinate manually, or you can use this
+plugin to compute it for you.
+
+In order to name the other series, you need to give it an id, like this:
+
+  var dataset = [
+    { data: [ ... ], id: "foo" } ,         // use default bottom
+    { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom
+  ];
+
+  $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }});
+
+As a convenience, if the id given is a number that doesn't appear as an id in
+the series, it is interpreted as the index in the array instead (so fillBetween:
+0 can also mean the first series).
+
+Internally, the plugin modifies the datapoints in each series. For line series,
+extra data points might be inserted through interpolation. Note that at points
+where the bottom line is not defined (due to a null point or start/end of line),
+the current line will show a gap too. The algorithm comes from the
+jquery.flot.stack.js plugin, possibly some code could be shared.
+
+*/
+
+(function ( $ ) {
+
+  var options = {
+    series: {
+      fillBetween: null // or number
+    }
+  };
+
+  function init( plot ) {
+
+    function findBottomSeries( s, allseries ) {
+
+      var i;
+
+      for ( i = 0; i < allseries.length; ++i ) {
+        if ( allseries[ i ].id === s.fillBetween ) {
+          return allseries[ i ];
+        }
+      }
+
+      if ( typeof s.fillBetween === "number" ) {
+        if ( s.fillBetween < 0 || s.fillBetween >= allseries.length ) {
+          return null;
+        }
+        return allseries[ s.fillBetween ];
+      }
+
+      return null;
+    }
+
+    function computeFillBottoms( plot, s, datapoints ) {
+      if ( s.fillBetween == null ) {
+        return;
+      }
+
+      var other = findBottomSeries( s, plot.getData() );
+
+      if ( !other ) {
+        return;
+      }
+
+      var ps = datapoints.pointsize,
+        points = datapoints.points,
+        otherps = other.datapoints.pointsize,
+        otherpoints = other.datapoints.points,
+        newpoints = [],
+        px, py, intery, qx, qy, bottom,
+        withlines = s.lines.show,
+        withbottom = ps > 2 && datapoints.format[2].y,
+        withsteps = withlines && s.lines.steps,
+        fromgap = true,
+        i = 0,
+        j = 0,
+        l, m;
+
+      while ( true ) {
+
+        if ( i >= points.length ) {
+          break;
+        }
+
+        l = newpoints.length;
+
+        if ( points[ i ] == null ) {
+
+          // copy gaps
+
+          for ( m = 0; m < ps; ++m ) {
+            newpoints.push( points[ i + m ] );
+          }
+
+          i += ps;
+
+        } else if ( j >= otherpoints.length ) {
+
+          // for lines, we can't use the rest of the points
+
+          if ( !withlines ) {
+            for ( m = 0; m < ps; ++m ) {
+              newpoints.push( points[ i + m ] );
+            }
+          }
+
+          i += ps;
+
+        } else if ( otherpoints[ j ] == null ) {
+
+          // oops, got a gap
+
+          for ( m = 0; m < ps; ++m ) {
+            newpoints.push( null );
+          }
+
+          fromgap = true;
+          j += otherps;
+
+        } else {
+
+          // cases where we actually got two points
+
+          px = points[ i ];
+          py = points[ i + 1 ];
+          qx = otherpoints[ j ];
+          qy = otherpoints[ j + 1 ];
+          bottom = 0;
+
+          if ( px === qx ) {
+
+            for ( m = 0; m < ps; ++m ) {
+              newpoints.push( points[ i + m ] );
+            }
+
+            //newpoints[ l + 1 ] += qy;
+            bottom = qy;
+
+            i += ps;
+            j += otherps;
+
+          } else if ( px > qx ) {
+
+            // we got past point below, might need to
+            // insert interpolated extra point
+
+            if ( withlines && i > 0 && points[ i - ps ] != null ) {
+              intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px );
+              newpoints.push( qx );
+              newpoints.push( intery );
+              for ( m = 2; m < ps; ++m ) {
+                newpoints.push( points[ i + m ] );
+              }
+              bottom = qy;
+            }
+
+            j += otherps;
+
+          } else { // px < qx
+
+            // if we come from a gap, we just skip this point
+
+            if ( fromgap && withlines ) {
+              i += ps;
+              continue;
+            }
+
+            for ( m = 0; m < ps; ++m ) {
+              newpoints.push( points[ i + m ] );
+            }
+
+            // we might be able to interpolate a point below,
+            // this can give us a better y
+
+            if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) {
+              bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx );
+            }
+
+            //newpoints[l + 1] += bottom;
+
+            i += ps;
+          }
+
+          fromgap = false;
+
+          if ( l !== newpoints.length && withbottom ) {
+            newpoints[ l + 2 ] = bottom;
+          }
+        }
+
+        // maintain the line steps invariant
+
+        if ( withsteps && l !== newpoints.length && l > 0 &&
+          newpoints[ l ] !== null &&
+          newpoints[ l ] !== newpoints[ l - ps ] &&
+          newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) {
+          for (m = 0; m < ps; ++m) {
+            newpoints[ l + ps + m ] = newpoints[ l + m ];
+          }
+          newpoints[ l + 1 ] = newpoints[ l - ps + 1 ];
+        }
+      }
+
+      datapoints.points = newpoints;
+    }
+
+    plot.hooks.processDatapoints.push( computeFillBottoms );
+  }
+
+  $.plot.plugins.push({
+    init: init,
+    options: options,
+    name: "fillbetween",
+    version: "1.0"
+  });
+
+})(jQuery);

+ 1 - 1
tasks/options/less.js

@@ -12,7 +12,7 @@ module.exports = function(config) {
     // Compile in place when not building
     src:{
       options: {
-        paths: ["<%= srcDir %>/vendor/bootstrap/less"],
+        paths: ["<%= srcDir %>/vendor/bootstrap/less", "<%= srcDir %>/css/less"],
         yuicompress:true
       },
       files: {

Некоторые файлы не были показаны из-за большого количества измененных файлов