bosun.org@v0.0.0-20250213104149-b8d3e981f37d/cmd/bosun/web/static/js/bosun.js (about)

     1  /// <reference path="moment.d.ts" />
     2  /// <reference path="moment-duration-format.d.ts" />
     3  //Represents an auth token
     4  var Token = /** @class */ (function () {
     5      function Token() {
     6          this.Description = "";
     7          this.Role = 0;
     8          this.User = "";
     9      }
    10      return Token;
    11  }());
    12  //metadata about a single role or permission
    13  var BitMeta = /** @class */ (function () {
    14      function BitMeta() {
    15      }
    16      return BitMeta;
    17  }());
    18  //all roles/permissions for bosun
    19  var RoleDefs = /** @class */ (function () {
    20      function RoleDefs() {
    21      }
    22      return RoleDefs;
    23  }());
    24  // See models/incident.go Event (can't be event here because JS uses that)
    25  var IncidentEvent = /** @class */ (function () {
    26      function IncidentEvent(ie) {
    27          this.Value = ie.Value;
    28          this.Expr = ie.Expr;
    29          this.Status = ie.Status;
    30          this.Time = ie.Time;
    31          this.Unevaluated = ie.Unevaluated;
    32      }
    33      return IncidentEvent;
    34  }());
    35  var Annotation = /** @class */ (function () {
    36      function Annotation(a, get) {
    37          a = a || {};
    38          this.Id = a.Id || "";
    39          this.Message = a.Message || "";
    40          this.StartDate = a.StartDate || "";
    41          this.EndDate = a.EndDate || "";
    42          this.CreationUser = a.CreationUser || "";
    43          this.Url = a.Url || "";
    44          this.Source = a.Source || "bosun-ui";
    45          this.Host = a.Host || "";
    46          this.Owner = a.Owner || !get && getOwner() || "";
    47          this.Category = a.Category || "";
    48      }
    49      Annotation.prototype.setTimeUTC = function () {
    50          var now = moment().utc().format(timeFormat);
    51          this.StartDate = now;
    52          this.EndDate = now;
    53      };
    54      Annotation.prototype.setTime = function () {
    55          var now = moment().format(timeFormat);
    56          this.StartDate = now;
    57          this.EndDate = now;
    58      };
    59      return Annotation;
    60  }());
    61  var Result = /** @class */ (function () {
    62      function Result(r) {
    63          this.Value = r.Value;
    64          this.Expr = r.Expr;
    65      }
    66      return Result;
    67  }());
    68  var Action = /** @class */ (function () {
    69      function Action(a) {
    70          this.User = a.User;
    71          this.Message = a.Message;
    72          this.Time = a.Time;
    73          this.Type = a.Type;
    74          this.Deadline = a.Deadline;
    75          this.Cancelled = a.Cancelled;
    76          this.Fullfilled = a.Fullfilled;
    77      }
    78      return Action;
    79  }());
    80  // See models/incident.go
    81  var IncidentState = /** @class */ (function () {
    82      function IncidentState(is) {
    83          this.Id = is.Id;
    84          this.Start = is.Start;
    85          this.End = is.End;
    86          this.AlertKey = is.AlertKey;
    87          this.Alert = is.Alert;
    88          this.Value = is.Value;
    89          this.Expr = is.Expr;
    90          this.Events = new Array();
    91          if (is.Events) {
    92              for (var _i = 0, _a = is.Events; _i < _a.length; _i++) {
    93                  var e = _a[_i];
    94                  this.Events.push(new IncidentEvent(e));
    95              }
    96          }
    97          this.Actions = new Array();
    98          this.Tags = is.Tags;
    99          if (is.Actions) {
   100              for (var _b = 0, _c = is.Actions; _b < _c.length; _b++) {
   101                  var a = _c[_b];
   102                  this.Actions.push(new Action(a));
   103              }
   104          }
   105          this.Subject = is.Subject;
   106          this.NeedAck = is.NeedAck;
   107          this.Open = is.Open;
   108          this.Unevaluated = is.Unevaluated;
   109          this.CurrentStatus = is.CurrentStatus;
   110          this.WorstStatus = is.WorstStatus;
   111          this.LastAbnormalStatus = is.LastAbnormalStatus;
   112          this.LastAbnormalTime = is.LastAbnormalTime;
   113          this.PreviousIds = new Array();
   114          if (is.PreviousIds) {
   115              for (var _d = 0, _e = is.PreviousIds; _d < _e.length; _d++) {
   116                  var id = _e[_d];
   117                  this.PreviousIds.push(id);
   118              }
   119          }
   120          this.NextId = is.NextId;
   121      }
   122      IncidentState.prototype.IsPendingClose = function () {
   123          for (var _i = 0, _a = this.Actions; _i < _a.length; _i++) {
   124              var action = _a[_i];
   125              if (action.Deadline != undefined && !(action.Fullfilled || action.Cancelled)) {
   126                  return true;
   127              }
   128          }
   129          return false;
   130      };
   131      return IncidentState;
   132  }());
   133  var StateGroup = /** @class */ (function () {
   134      function StateGroup(sg) {
   135          this.Active = sg.Active;
   136          this.Status = sg.Status;
   137          this.CurrentStatus = sg.CurrentStatus;
   138          this.Silenced = sg.Silenced;
   139          this.IsError = sg.IsError;
   140          this.Subject = sg.Subject;
   141          this.Alert = sg.Alert;
   142          this.AlertKey = sg.AlertKey;
   143          this.Ago = sg.Ago;
   144          if (sg.State) {
   145              this.State = new IncidentState(sg.State);
   146          }
   147          this.Children = new Array();
   148          if (sg.Children) {
   149              for (var _i = 0, _a = sg.Children; _i < _a.length; _i++) {
   150                  var c = _a[_i];
   151                  this.Children.push(new StateGroup(c));
   152              }
   153          }
   154      }
   155      return StateGroup;
   156  }());
   157  var Groups = /** @class */ (function () {
   158      function Groups(g) {
   159          this.NeedAck = new Array();
   160          if (g.NeedAck) {
   161              for (var _i = 0, _a = g.NeedAck; _i < _a.length; _i++) {
   162                  var sg = _a[_i];
   163                  this.NeedAck.push(new StateGroup(sg));
   164              }
   165          }
   166          this.Acknowledged = new Array();
   167          if (g.Acknowledged) {
   168              for (var _b = 0, _c = g.Acknowledged; _b < _c.length; _b++) {
   169                  var sg = _c[_b];
   170                  this.Acknowledged.push(new StateGroup(sg));
   171              }
   172          }
   173      }
   174      return Groups;
   175  }());
   176  var StateGroups = /** @class */ (function () {
   177      function StateGroups(sgs) {
   178          this.Groups = new Groups(sgs.Groups);
   179          this.TimeAndDate = sgs.TimeAndDate;
   180          this.FailingAlerts = sgs.FailingAlerts;
   181          this.UnclosedErrors = sgs.UnclosedErrors;
   182      }
   183      return StateGroups;
   184  }());
   185  /// <reference path="angular.d.ts" />
   186  /// <reference path="angular-route.d.ts" />
   187  /// <reference path="angular-sanitize.d.ts" />
   188  /// <reference path="bootstrap.d.ts" />
   189  /// <reference path="jquery.d.ts" />
   190  /// <reference path="underscore.d.ts" />
   191  /// <reference path="models.ts" />
   192  var bosunApp = angular.module('bosunApp', [
   193      'ngRoute',
   194      'bosunControllers',
   195      'mgcrea.ngStrap',
   196      'mgcrea.ngStrap.tooltip',
   197      'ngSanitize',
   198      'ui.ace',
   199      'ngclipboard',
   200  ]);
   201  bosunApp.config(['$routeProvider', '$locationProvider', '$httpProvider', function ($routeProvider, $locationProvider, $httpProvider) {
   202          $locationProvider.html5Mode({
   203              enabled: true,
   204              requireBase: false
   205          });
   206          var when = function (u, r) {
   207              $routeProvider.when(u, r);
   208          };
   209          when('/', {
   210              title: 'Dashboard',
   211              templateUrl: 'partials/dashboard.html',
   212              controller: 'DashboardCtrl',
   213          });
   214          when('/items', {
   215              title: 'Items',
   216              templateUrl: 'partials/items.html',
   217              controller: 'ItemsCtrl',
   218          });
   219          when('/expr', {
   220              title: 'Expression',
   221              templateUrl: 'partials/expr.html',
   222              controller: 'ExprCtrl',
   223          });
   224          when('/errors', {
   225              title: 'Errors',
   226              templateUrl: 'partials/errors.html',
   227              controller: 'ErrorCtrl',
   228          });
   229          when('/graph', {
   230              title: 'Graph',
   231              templateUrl: 'partials/graph.html',
   232              controller: 'GraphCtrl',
   233          });
   234          when('/host', {
   235              title: 'Host View',
   236              templateUrl: 'partials/host.html',
   237              controller: 'HostCtrl',
   238              reloadOnSearch: false,
   239          });
   240          when('/silence', {
   241              title: 'Silence',
   242              templateUrl: 'partials/silence.html',
   243              controller: 'SilenceCtrl',
   244          });
   245          when('/config', {
   246              title: 'Configuration',
   247              templateUrl: 'partials/config.html',
   248              controller: 'ConfigCtrl',
   249              reloadOnSearch: false,
   250          });
   251          when('/action', {
   252              title: 'Action',
   253              templateUrl: 'partials/action.html',
   254              controller: 'ActionCtrl',
   255          });
   256          when('/history', {
   257              title: 'Alert History',
   258              templateUrl: 'partials/history.html',
   259              controller: 'HistoryCtrl',
   260          });
   261          when('/put', {
   262              title: 'Data Entry',
   263              templateUrl: 'partials/put.html',
   264              controller: 'PutCtrl',
   265          });
   266          when('/annotation', {
   267              title: 'Annotation',
   268              templateUrl: 'partials/annotation.html',
   269              controller: 'AnnotationCtrl',
   270          });
   271          when('/incident', {
   272              title: 'Incident',
   273              templateUrl: 'partials/incident.html',
   274              controller: 'IncidentCtrl',
   275          });
   276          when('/tokens', {
   277              title: 'Access Tokens',
   278              template: "<token-list></token-list>",
   279          });
   280          when('/tokens/new', {
   281              title: 'New Access Token',
   282              template: "<new-token></new-token>",
   283          });
   284          $routeProvider.otherwise({
   285              redirectTo: '/',
   286          });
   287          $httpProvider.interceptors.push(function ($q) {
   288              return {
   289                  'request': function (config) {
   290                      config.headers['X-Miniprofiler'] = 'true';
   291                      return config;
   292                  },
   293              };
   294          });
   295      }]);
   296  bosunApp.run(['$location', '$rootScope', function ($location, $rootScope) {
   297          $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
   298              $rootScope.title = current.$$route.title;
   299              $rootScope.shortlink = false;
   300          });
   301      }]);
   302  var bosunControllers = angular.module('bosunControllers', []);
   303  bosunControllers.controller('BosunCtrl', ['$scope', '$route', '$http', '$q', '$rootScope', 'authService',
   304      function ($scope, $route, $http, $q, $rootScope, AuthService) {
   305          $scope.$on('$routeChangeSuccess', function (event, current, previous) {
   306              $scope.stop(true);
   307          });
   308          $scope.active = function (v) {
   309              if (!$route.current) {
   310                  return null;
   311              }
   312              if ($route.current.loadedTemplateUrl == 'partials/' + v + '.html') {
   313                  return { active: true };
   314              }
   315              return null;
   316          };
   317          $scope.init = function (settings) {
   318              $scope.saveEnabled = settings.SaveEnabled;
   319              $scope.annotateEnabled = settings.AnnotateEnabled;
   320              $scope.quiet = settings.Quiet;
   321              $scope.version = settings.Version;
   322              $scope.opentsdbEnabled = $scope.version.Major != 0 && $scope.version.Minor != 0;
   323              $scope.exampleExpression = settings.ExampleExpression;
   324              $scope.tokensEnabled = settings.TokensEnabled;
   325              $scope.auth = AuthService;
   326              AuthService.Init(settings.AuthEnabled, settings.Username, settings.Roles, settings.Permissions);
   327          };
   328          $scope.json = function (v) {
   329              return JSON.stringify(v, null, '  ');
   330          };
   331          $scope.btoa = function (v) {
   332              return encodeURIComponent(btoa(v));
   333          };
   334          $scope.encode = function (v) {
   335              return encodeURIComponent(v);
   336          };
   337          $scope.req_from_m = function (m) {
   338              var r = new GraphRequest();
   339              var q = new Query(false);
   340              q.metric = m;
   341              r.queries.push(q);
   342              return r;
   343          };
   344          $scope.panelClass = function (status, prefix) {
   345              if (prefix === void 0) { prefix = "panel-"; }
   346              switch (status) {
   347                  case "critical": return prefix + "danger";
   348                  case "unknown": return prefix + "info";
   349                  case "warning": return prefix + "warning";
   350                  case "normal": return prefix + "success";
   351                  case "error": return prefix + "danger";
   352                  default: return prefix + "default";
   353              }
   354          };
   355          $scope.values = {};
   356          $scope.setKey = function (key, value) {
   357              if (value === undefined) {
   358                  delete $scope.values[key];
   359              }
   360              else {
   361                  $scope.values[key] = value;
   362              }
   363          };
   364          $scope.getKey = function (key) {
   365              return $scope.values[key];
   366          };
   367          var scheduleFilter;
   368          $scope.refresh = function (filter) {
   369              var d = $q.defer();
   370              scheduleFilter = filter;
   371              $scope.animate();
   372              var p = $http.get('/api/alerts?filter=' + encodeURIComponent(filter || ""))
   373                  .success(function (data) {
   374                  $scope.schedule = new StateGroups(data);
   375                  $scope.timeanddate = data.TimeAndDate;
   376                  d.resolve();
   377              })
   378                  .error(function (err) {
   379                  d.reject(err);
   380              });
   381              p.finally($scope.stop);
   382              return d.promise;
   383          };
   384          // Size of the logo in (width and height) of the Bosun logo in the navbar
   385          var sz = 25;
   386          var orig = 700;
   387          var light = '#4ba2d9';
   388          var dark = '#1f5296';
   389          var med = '#356eb6';
   390          var mult = sz / orig;
   391          var bgrad = 25 * mult;
   392          var circles = [
   393              [150, 150, dark],
   394              [550, 150, dark],
   395              [150, 550, light],
   396              [550, 550, light],
   397          ];
   398          var svg = d3.select('#logo')
   399              .append('svg')
   400              .attr('height', sz)
   401              .attr('width', sz);
   402          svg.selectAll('rect.bg')
   403              .data([[0, light], [sz / 2, dark]])
   404              .enter()
   405              .append('rect')
   406              .attr('class', 'bg')
   407              .attr('width', sz)
   408              .attr('height', sz / 2)
   409              .attr('rx', bgrad)
   410              .attr('ry', bgrad)
   411              .attr('fill', function (d) { return d[1]; })
   412              .attr('y', function (d) { return d[0]; });
   413          svg.selectAll('path.diamond')
   414              .data([150, 550])
   415              .enter()
   416              .append('path')
   417              .attr('d', function (d) {
   418              var s = 'M ' + d * mult + ' ' + 150 * mult;
   419              var w = 200 * mult;
   420              s += ' l ' + w + ' ' + w;
   421              s += ' l ' + -w + ' ' + w;
   422              s += ' l ' + -w + ' ' + -w + ' Z';
   423              return s;
   424          })
   425              .attr('fill', med)
   426              .attr('stroke', 'white')
   427              .attr('stroke-width', 15 * mult);
   428          svg.selectAll('rect.white')
   429              .data([150, 350, 550])
   430              .enter()
   431              .append('rect')
   432              .attr('class', 'white')
   433              .attr('width', .5)
   434              .attr('height', '100%')
   435              .attr('fill', 'white')
   436              .attr('x', function (d) { return d * mult; });
   437          svg.selectAll('circle')
   438              .data(circles)
   439              .enter()
   440              .append('circle')
   441              .attr('cx', function (d) { return d[0] * mult; })
   442              .attr('cy', function (d) { return d[1] * mult; })
   443              .attr('r', 62.5 * mult)
   444              .attr('fill', function (d) { return d[2]; })
   445              .attr('stroke', 'white')
   446              .attr('stroke-width', 25 * mult);
   447          var transitionDuration = 750;
   448          var animateCount = 0;
   449          $scope.animate = function () {
   450              animateCount++;
   451              if (animateCount == 1) {
   452                  doAnimate();
   453              }
   454          };
   455          function doAnimate() {
   456              if (!animateCount) {
   457                  return;
   458              }
   459              d3.shuffle(circles);
   460              svg.selectAll('circle')
   461                  .data(circles, function (d, i) { return i; })
   462                  .transition()
   463                  .duration(transitionDuration)
   464                  .attr('cx', function (d) { return d[0] * mult; })
   465                  .attr('cy', function (d) { return d[1] * mult; })
   466                  .attr('fill', function (d) { return d[2]; });
   467              setTimeout(doAnimate, transitionDuration);
   468          }
   469          $scope.stop = function (all) {
   470              if (all === void 0) { all = false; }
   471              if (all) {
   472                  animateCount = 0;
   473              }
   474              else if (animateCount > 0) {
   475                  animateCount--;
   476              }
   477          };
   478          var short = $('#shortlink')[0];
   479          $scope.shorten = function () {
   480              $http.get('/api/shorten').success(function (data) {
   481                  if (data.id) {
   482                      short.value = data.id;
   483                      $rootScope.shortlink = true;
   484                      setTimeout(function () {
   485                          short.setSelectionRange(0, data.id.length);
   486                      });
   487                  }
   488              });
   489          };
   490      }]);
   491  var tsdbDateFormat = 'YYYY/MM/DD-HH:mm:ss';
   492  moment.defaultFormat = tsdbDateFormat;
   493  moment.locale('en', {
   494      relativeTime: {
   495          future: "in %s",
   496          past: "%s-ago",
   497          s: "%ds",
   498          m: "%dm",
   499          mm: "%dm",
   500          h: "%dh",
   501          hh: "%dh",
   502          d: "%dd",
   503          dd: "%dd",
   504          M: "%dn",
   505          MM: "%dn",
   506          y: "%dy",
   507          yy: "%dy"
   508      },
   509  });
   510  function ruleUrl(ak, fromTime) {
   511      var openBrack = ak.indexOf("{");
   512      var closeBrack = ak.indexOf("}");
   513      var alertName = ak.substr(0, openBrack);
   514      var template = ak.substring(openBrack + 1, closeBrack);
   515      var url = '/api/rule?' +
   516          'alert=' + encodeURIComponent(alertName) +
   517          '&from=' + encodeURIComponent(fromTime.format()) +
   518          '&template_group=' + encodeURIComponent(template);
   519      return url;
   520  }
   521  function configUrl(ak, fromTime) {
   522      var openBrack = ak.indexOf("{");
   523      var closeBrack = ak.indexOf("}");
   524      var alertName = ak.substr(0, openBrack);
   525      var template = ak.substring(openBrack + 1, closeBrack);
   526      // http://bosun/config?alert=haproxy.server.downtime.ny&fromDate=2016-07-10&fromTime=21%3A03
   527      var url = '/config?' +
   528          'alert=' + encodeURIComponent(alertName) +
   529          '&fromDate=' + encodeURIComponent(fromTime.format("YYYY-MM-DD")) +
   530          '&fromTime=' + encodeURIComponent(fromTime.format("HH:mm"));
   531      return url;
   532  }
   533  function createCookie(name, value, days) {
   534      var expires;
   535      if (days) {
   536          var date = new Date();
   537          date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
   538          expires = "; expires=" + date.toUTCString();
   539      }
   540      else {
   541          expires = "";
   542      }
   543      document.cookie = escape(name) + "=" + escape(value) + expires + "; path=/";
   544  }
   545  function readCookie(name) {
   546      var nameEQ = escape(name) + "=";
   547      var ca = document.cookie.split(';');
   548      for (var i = 0; i < ca.length; i++) {
   549          var c = ca[i];
   550          while (c.charAt(0) === ' ')
   551              c = c.substring(1, c.length);
   552          if (c.indexOf(nameEQ) === 0)
   553              return unescape(c.substring(nameEQ.length, c.length));
   554      }
   555      return null;
   556  }
   557  function eraseCookie(name) {
   558      createCookie(name, "", -1);
   559  }
   560  function getOwner() {
   561      return readCookie('action-owner');
   562  }
   563  function setOwner(name) {
   564      createCookie('action-owner', name, 1000);
   565  }
   566  function getShowAnnotations() {
   567      return readCookie('annotations-show');
   568  }
   569  function setShowAnnotations(yes) {
   570      createCookie('annotations-show', yes, 1000);
   571  }
   572  // from: http://stackoverflow.com/a/15267754/864236
   573  bosunApp.filter('reverse', function () {
   574      return function (items) {
   575          if (!angular.isArray(items)) {
   576              return [];
   577          }
   578          return items.slice().reverse();
   579      };
   580  });
   581  var timeFormat = 'YYYY-MM-DDTHH:mm:ssZ';
   582  /// <reference path="0-bosun.ts" />
   583  bosunControllers.controller('ExprCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
   584          var search = $location.search();
   585          var current = '';
   586          try {
   587              current = atob(search.expr);
   588          }
   589          catch (e) {
   590              current = '';
   591          }
   592          if (!current && $scope.exampleExpression) {
   593              $location.search('expr', btoa($scope.exampleExpression));
   594              return;
   595          }
   596          $scope.date = search.date || '';
   597          $scope.time = search.time || '';
   598          $scope.expr = current;
   599          $scope.aceMode = 'bosun';
   600          $scope.aceTheme = 'chrome';
   601          $scope.aceLoaded = function (editor) {
   602              $scope.editor = editor;
   603              editor.focus();
   604              editor.getSession().setUseWrapMode(true);
   605              editor.getSession().setMode({
   606                  path: 'ace/mode/' + $scope.aceMode,
   607                  v: Date.now()
   608              });
   609              editor.$blockScrolling = Infinity;
   610          };
   611          $scope.$on('$viewContentLoaded', function () {
   612              setTimeout(function () {
   613                  var editor = $scope.editor;
   614                  var row = editor.session.getLength() - 1;
   615                  var column = editor.session.getLine(row).length;
   616                  editor.selection.moveTo(row, column);
   617              });
   618          });
   619          $scope.tab = search.tab || 'results';
   620          if ($scope.expr) {
   621              $scope.running = $scope.expr;
   622              $scope.animate();
   623              $http.post('/api/expr?' +
   624                  'date=' + encodeURIComponent($scope.date) +
   625                  '&time=' + encodeURIComponent($scope.time), current)
   626                  .success(function (data) {
   627                  $scope.result = data.Results;
   628                  $scope.queries = data.Queries;
   629                  $scope.result_type = data.Type;
   630                  if (data.Type == 'series') {
   631                      $scope.svg_url = '/api/egraph/' + btoa(current) + '.svg?now=' + Math.floor(Date.now() / 1000);
   632                      $scope.graph = toChart(data.Results);
   633                  }
   634                  if (data.Type == 'number') {
   635                      angular.forEach(data.Results, function (d) {
   636                          var name = '{';
   637                          angular.forEach(d.Group, function (tagv, tagk) {
   638                              if (name.length > 1) {
   639                                  name += ',';
   640                              }
   641                              name += tagk + '=' + tagv;
   642                          });
   643                          name += '}';
   644                          d.name = name;
   645                      });
   646                      $scope.bar = data.Results;
   647                  }
   648                  $scope.running = '';
   649              })
   650                  .error(function (error) {
   651                  $scope.error = error;
   652                  $scope.running = '';
   653              })
   654                  .finally(function () {
   655                  $scope.stop();
   656              });
   657          }
   658          $scope.set = function () {
   659              $location.search('date', $scope.date || null);
   660              $location.search('time', $scope.time || null);
   661              if ($scope.expr) {
   662                  $location.search('expr', btoa($scope.expr));
   663                  $route.reload();
   664              }
   665              else {
   666                  $scope.error = "expr: empty";
   667                  $scope.result = null;
   668                  $scope.queries = null;
   669              }
   670          };
   671          function toChart(res) {
   672              var graph = [];
   673              angular.forEach(res, function (d, idx) {
   674                  var data = [];
   675                  angular.forEach(d.Value, function (val, ts) {
   676                      data.push([+ts, val]);
   677                  });
   678                  if (data.length == 0) {
   679                      return;
   680                  }
   681                  var name = '{';
   682                  angular.forEach(d.Group, function (tagv, tagk) {
   683                      if (name.length > 1) {
   684                          name += ',';
   685                      }
   686                      name += tagk + '=' + tagv;
   687                  });
   688                  name += '}';
   689                  var series = {
   690                      Data: data,
   691                      Name: name,
   692                  };
   693                  graph[idx] = series;
   694              });
   695              return graph;
   696          }
   697          $scope.keydown = function ($event) {
   698              if ($event.shiftKey && $event.keyCode == 13) {
   699                  $scope.set();
   700                  $event.preventDefault();
   701              }
   702          };
   703      }]);
   704  /// <reference path="expr.ts" />
   705  bosunControllers.controller('ActionCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
   706          var search = $location.search();
   707          $scope.type = search.type;
   708          $scope.activeIncidents = search.active == "true";
   709          $scope.notify = true;
   710          $scope.msgValid = true;
   711          $scope.message = "";
   712          $scope.duration = "";
   713          $scope.validateMsg = function () {
   714              $scope.msgValid = (!$scope.notify) || ($scope.message != "");
   715          };
   716          $scope.durationValid = true;
   717          $scope.validateDuration = function () {
   718              $scope.durationValid = $scope.duration == "" || parseDuration($scope.duration).asMilliseconds() != 0;
   719          };
   720          if (search.key) {
   721              var keys = search.key;
   722              if (!angular.isArray(search.key)) {
   723                  keys = [search.key];
   724              }
   725              $location.search('key', null);
   726              $scope.setKey('action-keys', keys);
   727          }
   728          else {
   729              $scope.keys = $scope.getKey('action-keys');
   730          }
   731          $scope.submit = function () {
   732              $scope.validateMsg();
   733              $scope.validateDuration();
   734              if (!$scope.msgValid || ($scope.user == "") || !$scope.durationValid) {
   735                  return;
   736              }
   737              var data = {
   738                  Type: $scope.type,
   739                  Message: $scope.message,
   740                  Keys: $scope.keys,
   741                  Notify: $scope.notify,
   742              };
   743              if ($scope.duration != "") {
   744                  data['Time'] = moment.utc().add(parseDuration($scope.duration));
   745              }
   746              $http.post('/api/action', data)
   747                  .success(function (data) {
   748                  $location.url('/');
   749              })
   750                  .error(function (error) {
   751                  alert(error);
   752              });
   753          };
   754      }]);
   755  /// <reference path="0-bosun.ts" />
   756  bosunControllers.controller('AnnotationCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
   757          var search = $location.search();
   758          $scope.id = search.id;
   759          if ($scope.id && $scope.id != "") {
   760              $http.get('/api/annotation/' + $scope.id)
   761                  .success(function (data) {
   762                  $scope.annotation = new Annotation(data, true);
   763                  $scope.error = "";
   764              })
   765                  .error(function (data) {
   766                  $scope.error = "failed to get annotation with id: " + $scope.id + ", error: " + data;
   767              });
   768          }
   769          else {
   770              $scope.annotation = new Annotation();
   771              $scope.annotation.setTimeUTC();
   772          }
   773          $http.get('/api/annotation/values/Owner')
   774              .success(function (data) {
   775              $scope.owners = data;
   776          });
   777          $http.get('/api/annotation/values/Category')
   778              .success(function (data) {
   779              $scope.categories = data;
   780          });
   781          $http.get('/api/annotation/values/Host')
   782              .success(function (data) {
   783              $scope.hosts = data;
   784          });
   785          $scope.submitAnnotation = function () {
   786              $scope.animate();
   787              $scope.annotation.CreationUser = $scope.auth.GetUsername();
   788              $http.post('/api/annotation', $scope.annotation)
   789                  .success(function (data) {
   790                  $scope.annotation = new Annotation(data, true);
   791                  $scope.error = "";
   792                  $scope.submitSuccess = true;
   793                  $scope.deleteSuccess = false;
   794              })
   795                  .error(function (error) {
   796                  $scope.error = "failed to create annotation: " + error.error;
   797                  $scope.submitSuccess = false;
   798              })
   799                  .finally(function () {
   800                  $scope.stop();
   801              });
   802          };
   803          $scope.deleteAnnotation = function () {
   804              $scope.animate();
   805              $http.delete('/api/annotation/' + $scope.annotation.Id)
   806                  .success(function (data) {
   807                  $scope.error = "";
   808                  $scope.deleteSuccess = true;
   809                  $scope.submitSuccess = false;
   810                  $scope.annotation = new (Annotation);
   811                  $scope.annotation.setTimeUTC();
   812              })
   813                  .error(function (error) {
   814                  $scope.error = "failed to delete annotation with id: " + $scope.annotation.Id + ", error: " + error.error;
   815                  $scope.deleteSuccess = false;
   816              })
   817                  .finally(function () {
   818                  $scope.stop();
   819              });
   820          };
   821      }]);
   822  /// <reference path="0-bosun.ts" />
   823  var AuthService = /** @class */ (function () {
   824      function AuthService() {
   825      }
   826      AuthService.prototype.Init = function (authEnabled, username, roles, userPerms) {
   827          this.roles = roles;
   828          this.username = username;
   829          this.userPerms = userPerms;
   830          this.authEnabled = authEnabled;
   831          this.cleanRoles();
   832          if (!authEnabled) {
   833              var cookVal = readCookie("action-user");
   834              if (cookVal) {
   835                  this.username = cookVal;
   836              }
   837          }
   838      };
   839      AuthService.prototype.HasPermission = function (s) {
   840          for (var _i = 0, _a = this.roles.Permissions; _i < _a.length; _i++) {
   841              var p = _a[_i];
   842              if (p.Name == s) {
   843                  return (p.Bits & this.userPerms) != 0;
   844              }
   845          }
   846          return true;
   847      };
   848      AuthService.prototype.PermissionsFor = function (bits) {
   849          if (bits == null) {
   850              bits = this.userPerms;
   851          }
   852          var perms = [];
   853          for (var _i = 0, _a = this.roles.Permissions; _i < _a.length; _i++) {
   854              var p = _a[_i];
   855              if (p.Bits & bits) {
   856                  perms.push(p.Name);
   857              }
   858          }
   859          return perms;
   860      };
   861      AuthService.prototype.RoleFor = function (bits) {
   862          if (bits == null) {
   863              bits = this.userPerms;
   864          }
   865          var perms = [];
   866          for (var _i = 0, _a = this.roles.Roles; _i < _a.length; _i++) {
   867              var r = _a[_i];
   868              if (r.Bits == bits) {
   869                  return r.Name;
   870              }
   871          }
   872          return null;
   873      };
   874      AuthService.prototype.GetRoles = function () {
   875          return this.roles;
   876      };
   877      AuthService.prototype.Username = function (u) {
   878          if (!this.authEnabled && angular.isDefined(u)) {
   879              this.username = u;
   880              createCookie("action-user", u, 90);
   881          }
   882          return this.username;
   883      };
   884      AuthService.prototype.GetUsername = function () {
   885          return this.username;
   886      };
   887      AuthService.prototype.Enabled = function () {
   888          return this.authEnabled;
   889      };
   890      AuthService.prototype.cleanRoles = function () {
   891          var _this = this;
   892          //fix admin role that has extra bits corresponding to future permissions.
   893          //causes bit math to go crazy and overflow. 
   894          //prevents easily  making tokens that grant unknown future perms too.
   895          _(this.roles.Roles).each(function (role) {
   896              var mask = 0;
   897              _(_this.roles.Permissions).each(function (p) {
   898                  if ((p.Bits & role.Bits) != 0) {
   899                      mask |= p.Bits;
   900                  }
   901              });
   902              role.Bits = mask;
   903          });
   904      };
   905      return AuthService;
   906  }());
   907  bosunApp.service("authService", AuthService);
   908  //simple component to show a <username-input> easily
   909  var UsernameInputController = /** @class */ (function () {
   910      function UsernameInputController(auth) {
   911          this.auth = auth;
   912      }
   913      UsernameInputController.$inject = ['authService'];
   914      return UsernameInputController;
   915  }());
   916  bosunApp.component("usernameInput", {
   917      controller: UsernameInputController,
   918      controllerAs: "ct",
   919      template: '<input type="text"class="form-control"  ng-disabled="ct.auth.Enabled()" ng-model="ct.auth.Username" ng-model-options="{ getterSetter: true }">',
   920  });
   921  /// <reference path="0-bosun.ts" />
   922  bosunControllers.controller('ConfigCtrl', ['$scope', '$http', '$location', '$route', '$timeout', '$sce', function ($scope, $http, $location, $route, $timeout, $sce) {
   923          var search = $location.search();
   924          $scope.fromDate = search.fromDate || '';
   925          $scope.fromTime = search.fromTime || '';
   926          $scope.toDate = search.toDate || '';
   927          $scope.toTime = search.toTime || '';
   928          $scope.intervals = +search.intervals || 5;
   929          $scope.duration = +search.duration || null;
   930          $scope.runningHash = search.runningHash || null;
   931          $scope.runningChanged = search.runningChanged || false;
   932          $scope.config_text = 'Loading config...';
   933          $scope.selected_alert = search.alert || '';
   934          $scope.email = search.email || '';
   935          $scope.template_group = search.template_group || '';
   936          $scope.items = parseItems();
   937          $scope.tab = search.tab || 'results';
   938          $scope.aceTheme = 'chrome';
   939          $scope.actionTypeToShow = "Acknowledged";
   940          $scope.incidentId = 42;
   941          $scope.aceMode = 'bosun';
   942          $scope.expandDiff = false;
   943          $scope.customTemplates = {};
   944          $scope.runningChangedHelp = "The running config has been changed. This means you are in danger of overwriting someone else's changes. To view the changes open the 'Save Dialogue' and you will see a unified diff. The only way to get rid of the error panel is to open a new instance of the rule editor and copy your changes into it. You are still permitted to save without doing this, but then you must be very careful not to overwrite anyone else's changes.";
   945          $scope.sectionToDocs = {
   946              "alert": "https://bosun.org/definitions#alert-definitions",
   947              "template": "https://bosun.org/definitions#templates",
   948              "lookup": "https://bosun.org/definitions#lookup-tables",
   949              "notification": "https://bosun.org/definitions#notifications",
   950              "macro": "https://bosun.org/definitions#macros"
   951          };
   952          var expr = search.expr;
   953          function buildAlertFromExpr() {
   954              if (!expr)
   955                  return;
   956              var newAlertName = "test";
   957              var idx = 1;
   958              //find a unique alert name
   959              while ($scope.items["alert"].indexOf(newAlertName) != -1 || $scope.items["template"].indexOf(newAlertName) != -1) {
   960                  newAlertName = "test" + idx;
   961                  idx++;
   962              }
   963              var text = '\n\ntemplate ' + newAlertName + ' {\n' +
   964                  '	subject = {{.Last.Status}}: {{.Alert.Name}} on {{.Group.host}}\n' +
   965                  '	body = `<p>Name: {{.Alert.Name}}\n' +
   966                  '	<p>Tags:\n' +
   967                  '	<table>\n' +
   968                  '		{{range $k, $v := .Group}}\n' +
   969                  '			<tr><td>{{$k}}</td><td>{{$v}}</td></tr>\n' +
   970                  '		{{end}}\n' +
   971                  '	</table>`\n' +
   972                  '}\n\n';
   973              var expression = atob(expr);
   974              var lines = expression.split("\n").map(function (l) { return l.trim(); });
   975              lines[lines.length - 1] = "crit = " + lines[lines.length - 1];
   976              expression = lines.join("\n    ");
   977              text += 'alert ' + newAlertName + ' {\n' +
   978                  '	template = ' + newAlertName + '\n' +
   979                  '	' + expression + '\n' +
   980                  '}\n';
   981              $scope.config_text += text;
   982              $scope.items = parseItems();
   983              $timeout(function () {
   984                  //can't scroll editor until after control is updated. Defer it.
   985                  $scope.scrollTo("alert", newAlertName);
   986              });
   987          }
   988          function parseItems() {
   989              var configText = $scope.config_text;
   990              var re = /^\s*(alert|template|notification|lookup|macro)\s+([\w\-\.\$]+)\s*\{/gm;
   991              var match;
   992              var items = {};
   993              items["alert"] = [];
   994              items["template"] = [];
   995              items["lookup"] = [];
   996              items["notification"] = [];
   997              items["macro"] = [];
   998              while (match = re.exec(configText)) {
   999                  var type = match[1];
  1000                  var name = match[2];
  1001                  var list = items[type];
  1002                  if (!list) {
  1003                      list = [];
  1004                      items[type] = list;
  1005                  }
  1006                  list.push(name);
  1007              }
  1008              return items;
  1009          }
  1010          $http.get('/api/config?hash=' + encodeURIComponent(search.hash || ''))
  1011              .success(function (data) {
  1012              $scope.config_text = data;
  1013              $scope.items = parseItems();
  1014              buildAlertFromExpr();
  1015              if (!$scope.selected_alert && $scope.items["alert"].length) {
  1016                  $scope.selected_alert = $scope.items["alert"][0];
  1017              }
  1018              $timeout(function () {
  1019                  //can't scroll editor until after control is updated. Defer it.
  1020                  $scope.scrollTo("alert", $scope.selected_alert);
  1021              });
  1022          })
  1023              .error(function (data) {
  1024              $scope.validationResult = "Error fetching config: " + data;
  1025          });
  1026          $scope.reparse = function () {
  1027              $scope.items = parseItems();
  1028          };
  1029          var editor;
  1030          $scope.aceLoaded = function (_editor) {
  1031              editor = _editor;
  1032              $scope.editor = editor;
  1033              editor.focus();
  1034              editor.getSession().setUseWrapMode(true);
  1035              editor.on("blur", function () {
  1036                  $scope.$apply(function () {
  1037                      $scope.items = parseItems();
  1038                  });
  1039              });
  1040          };
  1041          var syntax = true;
  1042          $scope.aceToggleHighlight = function () {
  1043              if (syntax) {
  1044                  editor.getSession().setMode();
  1045                  syntax = false;
  1046                  return;
  1047              }
  1048              syntax = true;
  1049              editor.getSession().setMode({
  1050                  path: 'ace/mode/' + $scope.aceMode,
  1051                  v: Date.now()
  1052              });
  1053          };
  1054          $scope.scrollTo = function (type, name) {
  1055              var searchRegex = new RegExp("^\\s*" + type + "\\s+" + name, "g");
  1056              editor.find(searchRegex, {
  1057                  backwards: false,
  1058                  wrap: true,
  1059                  caseSensitive: false,
  1060                  wholeWord: false,
  1061                  regExp: true,
  1062              });
  1063              if (type == "alert") {
  1064                  $scope.selectAlert(name);
  1065              }
  1066          };
  1067          $scope.scrollToInterval = function (id) {
  1068              document.getElementById('time-' + id).scrollIntoView();
  1069              $scope.show($scope.sets[id]);
  1070          };
  1071          $scope.show = function (set) {
  1072              set.show = 'loading...';
  1073              $scope.animate();
  1074              var url = '/api/rule?' +
  1075                  'alert=' + encodeURIComponent($scope.selected_alert) +
  1076                  '&from=' + encodeURIComponent(set.Time);
  1077              $http.post(url, $scope.config_text)
  1078                  .success(function (data) {
  1079                  procResults(data);
  1080                  set.Results = data.Sets[0].Results;
  1081              })
  1082                  .error(function (error) {
  1083                  $scope.errors = [error];
  1084              })
  1085                  .finally(function () {
  1086                  $scope.stop();
  1087                  delete (set.show);
  1088              });
  1089          };
  1090          $scope.getRunningHash = function () {
  1091              if (!$scope.saveEnabled) {
  1092                  return;
  1093              }
  1094              (function tick() {
  1095                  $http.get('/api/config/running_hash')
  1096                      .success(function (data) {
  1097                      $scope.runningHashResult = '';
  1098                      $timeout(tick, 15 * 1000);
  1099                      if ($scope.runningHash) {
  1100                          if (data.Hash != $scope.runningHash) {
  1101                              $scope.runningChanged = true;
  1102                              return;
  1103                          }
  1104                      }
  1105                      $scope.runningHash = data.Hash;
  1106                      $scope.runningChanged = false;
  1107                  })
  1108                      .error(function (data) {
  1109                      $scope.runningHashResult = "Error getting running config hash: " + data;
  1110                  });
  1111              })();
  1112          };
  1113          $scope.getRunningHash();
  1114          $scope.setInterval = function () {
  1115              var from = moment.utc($scope.fromDate + ' ' + $scope.fromTime);
  1116              var to = moment.utc($scope.toDate + ' ' + $scope.toTime);
  1117              if (!from.isValid() || !to.isValid()) {
  1118                  return;
  1119              }
  1120              var diff = from.diff(to);
  1121              if (!diff) {
  1122                  return;
  1123              }
  1124              var intervals = +$scope.intervals;
  1125              if (intervals < 2) {
  1126                  return;
  1127              }
  1128              diff /= 1000 * 60;
  1129              var d = Math.abs(Math.round(diff / intervals));
  1130              if (d < 1) {
  1131                  d = 1;
  1132              }
  1133              $scope.duration = d;
  1134          };
  1135          $scope.setDuration = function () {
  1136              var from = moment.utc($scope.fromDate + ' ' + $scope.fromTime);
  1137              var to = moment.utc($scope.toDate + ' ' + $scope.toTime);
  1138              if (!from.isValid() || !to.isValid()) {
  1139                  return;
  1140              }
  1141              var diff = from.diff(to);
  1142              if (!diff) {
  1143                  return;
  1144              }
  1145              var duration = +$scope.duration;
  1146              if (duration < 1) {
  1147                  return;
  1148              }
  1149              $scope.intervals = Math.abs(Math.round(diff / duration / 1000 / 60));
  1150          };
  1151          $scope.selectAlert = function (alert) {
  1152              $scope.selected_alert = alert;
  1153              $location.search("alert", alert);
  1154              // Attempt to find `template = foo` in order to set up quick jump between template and alert
  1155              var searchRegex = new RegExp("^\\s*alert\\s+" + alert, "g");
  1156              var lines = $scope.config_text.split("\n");
  1157              $scope.quickJumpTarget = null;
  1158              for (var i = 0; i < lines.length; i++) {
  1159                  if (searchRegex.test(lines[i])) {
  1160                      for (var j = i + 1; j < lines.length; j++) {
  1161                          // Close bracket at start of line means end of alert.
  1162                          if (/^\s*\}/m.test(lines[j])) {
  1163                              return;
  1164                          }
  1165                          var found = /^\s*template\s*=\s*([\w\-\.\$]+)/m.exec(lines[j]);
  1166                          if (found) {
  1167                              $scope.quickJumpTarget = "template " + found[1];
  1168                          }
  1169                      }
  1170                  }
  1171              }
  1172          };
  1173          $scope.quickJump = function () {
  1174              var parts = $scope.quickJumpTarget.split(" ");
  1175              if (parts.length != 2) {
  1176                  return;
  1177              }
  1178              $scope.scrollTo(parts[0], parts[1]);
  1179              if (parts[0] == "template" && $scope.selected_alert) {
  1180                  $scope.quickJumpTarget = "alert " + $scope.selected_alert;
  1181              }
  1182          };
  1183          $scope.setTemplateGroup = function (group) {
  1184              var match = group.match(/{(.*)}/);
  1185              if (match) {
  1186                  $scope.template_group = match[1];
  1187              }
  1188          };
  1189          $scope.setNotificationToShow = function (n) {
  1190              $scope.notificationToShow = n;
  1191          };
  1192          var line_re = /test:(\d+)/;
  1193          $scope.validate = function () {
  1194              $http.post('/api/config_test', $scope.config_text)
  1195                  .success(function (data) {
  1196                  if (data == "") {
  1197                      $scope.validationResult = "Valid";
  1198                      $timeout(function () {
  1199                          $scope.validationResult = "";
  1200                      }, 2000);
  1201                  }
  1202                  else {
  1203                      $scope.validationResult = data;
  1204                      var m = data.match(line_re);
  1205                      if (angular.isArray(m) && (m.length > 1)) {
  1206                          editor.gotoLine(m[1]);
  1207                      }
  1208                  }
  1209              })
  1210                  .error(function (error) {
  1211                  $scope.validationResult = 'Error validating: ' + error;
  1212              });
  1213          };
  1214          $scope.test = function () {
  1215              $scope.errors = [];
  1216              $scope.running = true;
  1217              $scope.warning = [];
  1218              $location.search('fromDate', $scope.fromDate || null);
  1219              $location.search('fromTime', $scope.fromTime || null);
  1220              $location.search('toDate', $scope.toDate || null);
  1221              $location.search('toTime', $scope.toTime || null);
  1222              $location.search('intervals', String($scope.intervals) || null);
  1223              $location.search('duration', String($scope.duration) || null);
  1224              $location.search('email', $scope.email || null);
  1225              $location.search('template_group', $scope.template_group || null);
  1226              $location.search('runningHash', $scope.runningHash);
  1227              $location.search('runningChanged', $scope.runningChanged);
  1228              $scope.animate();
  1229              var from = moment.utc($scope.fromDate + ' ' + $scope.fromTime);
  1230              var to = moment.utc($scope.toDate + ' ' + $scope.toTime);
  1231              if (!from.isValid()) {
  1232                  from = to;
  1233              }
  1234              if (!to.isValid()) {
  1235                  to = from;
  1236              }
  1237              if (!from.isValid() && !to.isValid()) {
  1238                  from = to = moment.utc();
  1239              }
  1240              var diff = from.diff(to);
  1241              var intervals;
  1242              if (diff == 0) {
  1243                  intervals = 1;
  1244              }
  1245              else if (Math.abs(diff) < 60 * 1000) { // 1 minute
  1246                  intervals = 2;
  1247              }
  1248              else {
  1249                  intervals = +$scope.intervals;
  1250              }
  1251              var url = '/api/rule?' +
  1252                  'alert=' + encodeURIComponent($scope.selected_alert) +
  1253                  '&from=' + encodeURIComponent(from.format()) +
  1254                  '&to=' + encodeURIComponent(to.format()) +
  1255                  '&intervals=' + encodeURIComponent(intervals) +
  1256                  '&email=' + encodeURIComponent($scope.email) +
  1257                  '&incidentId=' + $scope.incidentId +
  1258                  '&template_group=' + encodeURIComponent($scope.template_group);
  1259              $http.post(url, $scope.config_text)
  1260                  .success(function (data) {
  1261                  $scope.sets = data.Sets;
  1262                  $scope.alert_history = data.AlertHistory;
  1263                  if (data.Hash) {
  1264                      $location.search('hash', data.Hash);
  1265                  }
  1266                  procResults(data);
  1267              })
  1268                  .error(function (error) {
  1269                  $scope.errors = [error];
  1270              })
  1271                  .finally(function () {
  1272                  $scope.running = false;
  1273                  $scope.stop();
  1274              });
  1275          };
  1276          $scope.zws = function (v) {
  1277              return v.replace(/([,{}()])/g, '$1\u200b');
  1278          };
  1279          $scope.loadTimelinePanel = function (entry, v) {
  1280              if (v.doneLoading && !v.error) {
  1281                  return;
  1282              }
  1283              v.error = null;
  1284              v.doneLoading = false;
  1285              var ak = entry.key;
  1286              var openBrack = ak.indexOf("{");
  1287              var closeBrack = ak.indexOf("}");
  1288              var alertName = ak.substr(0, openBrack);
  1289              var template = ak.substring(openBrack + 1, closeBrack);
  1290              var url = '/api/rule?' +
  1291                  'alert=' + encodeURIComponent(alertName) +
  1292                  '&from=' + encodeURIComponent(moment.utc(v.Time).format()) +
  1293                  '&template_group=' + encodeURIComponent(template);
  1294              $http.post(url, $scope.config_text)
  1295                  .success(function (data) {
  1296                  v.subject = data.Subject;
  1297                  v.body = $sce.trustAsHtml(data.Body);
  1298              })
  1299                  .error(function (error) {
  1300                  v.error = error;
  1301              })
  1302                  .finally(function () {
  1303                  v.doneLoading = true;
  1304              });
  1305          };
  1306          function procResults(data) {
  1307              $scope.subject = data.Subject;
  1308              $scope.body = $sce.trustAsHtml(data.Body);
  1309              if (data.EmailSubject) {
  1310                  data.EmailSubject = atob(data.EmailSubject);
  1311              }
  1312              $scope.emailSubject = data.EmailSubject;
  1313              if (data.EmailBody) {
  1314                  data.EmailBody = atob(data.EmailBody);
  1315              }
  1316              $scope.emailBody = $sce.trustAsHtml(data.EmailBody);
  1317              $scope.customTemplates = {};
  1318              for (var k in data.Custom) {
  1319                  $scope.customTemplates[k] = data.Custom[k];
  1320              }
  1321              var nots = {};
  1322              _(data.Notifications).each(function (val, n) {
  1323                  if (val.Email) {
  1324                      nots["Email " + n] = val.Email;
  1325                  }
  1326                  if (val.Print != "") {
  1327                      nots["Print " + n] = { Print: val.Print };
  1328                  }
  1329                  _(val.HTTP).each(function (hp) {
  1330                      nots[hp.Method + " " + n] = hp;
  1331                  });
  1332              });
  1333              $scope.notifications = nots;
  1334              var aNots = {};
  1335              _(data.ActionNotifications).each(function (ts, n) {
  1336                  $scope.notificationToShow = "" + n;
  1337                  aNots[n] = {};
  1338                  _(ts).each(function (val, at) {
  1339                      if (val.Email) {
  1340                          aNots[n]["Email (" + at + ")"] = val.Email;
  1341                      }
  1342                      _(val.HTTP).each(function (hp) {
  1343                          aNots[n][hp.Method + " (" + at + ")"] = hp;
  1344                      });
  1345                  });
  1346              });
  1347              $scope.actionNotifications = aNots;
  1348              $scope.data = JSON.stringify(data.Data, null, '  ');
  1349              $scope.errors = data.Errors;
  1350              $scope.warning = data.Warnings;
  1351          }
  1352          $scope.downloadConfig = function () {
  1353              var blob = new Blob([$scope.config_text], { type: "text/plain;charset=utf-8" });
  1354              saveAs(blob, "bosun.conf");
  1355          };
  1356          $scope.diffConfig = function () {
  1357              $http.post('/api/config/diff', {
  1358                  "Config": $scope.config_text,
  1359                  "Message": $scope.message
  1360              })
  1361                  .success(function (data) {
  1362                  $scope.diff = data || "No Diff";
  1363                  // Reset running hash if there is no difference?
  1364              })
  1365                  .error(function (error) {
  1366                  $scope.diff = "Failed to load diff: " + error;
  1367              });
  1368          };
  1369          $scope.saveConfig = function () {
  1370              if (!$scope.saveEnabled) {
  1371                  return;
  1372              }
  1373              $scope.saveResult = "Saving; Please Wait";
  1374              $http.post('/api/config/save', {
  1375                  "Config": $scope.config_text,
  1376                  "Diff": $scope.diff,
  1377                  "Message": $scope.message
  1378              })
  1379                  .success(function (data) {
  1380                  $scope.saveResult = "Config Saved; Reloading";
  1381                  $scope.runningHash = undefined;
  1382              })
  1383                  .error(function (error) {
  1384                  $scope.saveResult = error;
  1385              });
  1386          };
  1387          $scope.saveClass = function () {
  1388              if ($scope.saveResult == "Saving; Please Wait") {
  1389                  return "alert-warning";
  1390              }
  1391              if ($scope.saveResult == "Config Saved; Reloading") {
  1392                  return "alert-success";
  1393              }
  1394              return "alert-danger";
  1395          };
  1396          return $scope;
  1397      }]);
  1398  var NotificationController = /** @class */ (function () {
  1399      function NotificationController($http) {
  1400          var _this = this;
  1401          this.$http = $http;
  1402          this.test = function () {
  1403              _this.dat.msg = "sending";
  1404              _this.$http.post('/api/rule/notification/test', _this.dat)
  1405                  .success(function (rDat) {
  1406                  if (rDat.Error) {
  1407                      _this.dat.msg = "Error: " + rDat.Error;
  1408                  }
  1409                  else {
  1410                      _this.dat.msg = "Success! Status Code: " + rDat.Status;
  1411                  }
  1412              })
  1413                  .error(function (error) {
  1414                  _this.dat.msg = "Error: " + error;
  1415              });
  1416          };
  1417      }
  1418      NotificationController.$inject = ['$http'];
  1419      return NotificationController;
  1420  }());
  1421  bosunApp.component('notification', {
  1422      bindings: {
  1423          dat: "<",
  1424      },
  1425      controller: NotificationController,
  1426      controllerAs: 'ct',
  1427      templateUrl: '/static/partials/notification.html',
  1428  });
  1429  bosunControllers.controller('DashboardCtrl', ['$scope', '$http', '$location', function ($scope, $http, $location) {
  1430          var search = $location.search();
  1431          $scope.loading = 'Loading';
  1432          $scope.error = '';
  1433          $scope.filter = search.filter;
  1434          if (!$scope.filter) {
  1435              $scope.filter = readCookie("filter");
  1436          }
  1437          $location.search('filter', $scope.filter || null);
  1438          reload();
  1439          function reload() {
  1440              $scope.refresh($scope.filter).then(function () {
  1441                  $scope.loading = '';
  1442                  $scope.error = '';
  1443              }, function (err) {
  1444                  $scope.loading = '';
  1445                  $scope.error = 'Unable to fetch alerts: ' + err;
  1446              });
  1447          }
  1448          $scope.keydown = function ($event) {
  1449              if ($event.keyCode == 13) {
  1450                  createCookie("filter", $scope.filter || "", 1000);
  1451                  $location.search('filter', $scope.filter || null);
  1452              }
  1453          };
  1454      }]);
  1455  /// <reference path="0-bosun.ts" />
  1456  bosunApp.directive('tsResults', function () {
  1457      return {
  1458          templateUrl: '/partials/results.html',
  1459          link: function (scope, elem, attrs) {
  1460              scope.isSeries = function (v) {
  1461                  return typeof (v) === 'object';
  1462              };
  1463          },
  1464      };
  1465  });
  1466  bosunApp.directive('tsComputations', function () {
  1467      return {
  1468          scope: {
  1469              computations: '=tsComputations',
  1470              time: '=',
  1471              header: '=',
  1472          },
  1473          templateUrl: '/partials/computations.html',
  1474          link: function (scope, elem, attrs) {
  1475              if (scope.time) {
  1476                  var m = moment.utc(scope.time);
  1477                  scope.timeParam = "&date=" + encodeURIComponent(m.format("YYYY-MM-DD")) + "&time=" + encodeURIComponent(m.format("HH:mm"));
  1478              }
  1479              scope.btoa = function (v) {
  1480                  return encodeURIComponent(btoa(v));
  1481              };
  1482          },
  1483      };
  1484  });
  1485  function fmtDuration(v) {
  1486      var diff = (moment.duration(v, 'milliseconds'));
  1487      var f;
  1488      if (Math.abs(v) < 60000) {
  1489          return diff.format('ss[s]');
  1490      }
  1491      return diff.format('d[d]hh[h]mm[m]ss[s]');
  1492  }
  1493  function fmtTime(v) {
  1494      var m = moment(v).utc();
  1495      var now = moment().utc();
  1496      var msdiff = now.diff(m);
  1497      var ago = '';
  1498      var inn = '';
  1499      if (msdiff >= 0) {
  1500          ago = ' ago';
  1501      }
  1502      else {
  1503          inn = 'in ';
  1504      }
  1505      return m.format() + ' UTC (' + inn + fmtDuration(Math.abs(msdiff)) + ago + ')';
  1506  }
  1507  function parseDuration(v) {
  1508      var pattern = /(\d+)(d|y|n|h|m|s)(-ago)?/;
  1509      var m = pattern.exec(v);
  1510      if (m) {
  1511          return moment.duration(parseInt(m[1]), m[2].replace('n', 'M'));
  1512      }
  1513      return moment.duration(0);
  1514  }
  1515  bosunApp.directive("tsTime", function () {
  1516      return {
  1517          link: function (scope, elem, attrs) {
  1518              scope.$watch(attrs.tsTime, function (v) {
  1519                  var m = moment(v).utc();
  1520                  var text = fmtTime(v);
  1521                  if (attrs.tsEndTime) {
  1522                      var diff = moment(scope.$eval(attrs.tsEndTime)).diff(m);
  1523                      var duration = fmtDuration(diff);
  1524                      text += " for " + duration;
  1525                  }
  1526                  if (attrs.noLink) {
  1527                      elem.text(text);
  1528                  }
  1529                  else {
  1530                      var el = document.createElement('a');
  1531                      el.text = text;
  1532                      el.href = 'http://www.timeanddate.com/worldclock/converted.html?iso=';
  1533                      el.href += m.format('YYYYMMDDTHHmm');
  1534                      el.href += '&p1=0';
  1535                      angular.forEach(scope.timeanddate, function (v, k) {
  1536                          el.href += '&p' + (k + 2) + '=' + v;
  1537                      });
  1538                      elem.html(el);
  1539                  }
  1540              });
  1541          },
  1542      };
  1543  });
  1544  bosunApp.directive("tsTimeUnix", function () {
  1545      return {
  1546          link: function (scope, elem, attrs) {
  1547              scope.$watch(attrs.tsTimeUnix, function (v) {
  1548                  var m = moment(v * 1000).utc();
  1549                  var text = fmtTime(m);
  1550                  if (attrs.tsEndTime) {
  1551                      var diff = moment(scope.$eval(attrs.tsEndTime)).diff(m);
  1552                      var duration = fmtDuration(diff);
  1553                      text += " for " + duration;
  1554                  }
  1555                  if (attrs.noLink) {
  1556                      elem.text(text);
  1557                  }
  1558                  else {
  1559                      var el = document.createElement('a');
  1560                      el.text = text;
  1561                      el.href = 'http://www.timeanddate.com/worldclock/converted.html?iso=';
  1562                      el.href += m.format('YYYYMMDDTHHmm');
  1563                      el.href += '&p1=0';
  1564                      angular.forEach(scope.timeanddate, function (v, k) {
  1565                          el.href += '&p' + (k + 2) + '=' + v;
  1566                      });
  1567                      elem.html(el);
  1568                  }
  1569              });
  1570          },
  1571      };
  1572  });
  1573  bosunApp.directive("tsSince", function () {
  1574      return {
  1575          link: function (scope, elem, attrs) {
  1576              scope.$watch(attrs.tsSince, function (v) {
  1577                  var m = moment(v).utc();
  1578                  elem.text(m.fromNow());
  1579              });
  1580          },
  1581      };
  1582  });
  1583  bosunApp.directive("tooltip", function () {
  1584      return {
  1585          link: function (scope, elem, attrs) {
  1586              angular.element(elem[0]).tooltip({ placement: "bottom" });
  1587          },
  1588      };
  1589  });
  1590  bosunApp.directive('tsTab', function () {
  1591      return {
  1592          link: function (scope, elem, attrs) {
  1593              var ta = elem[0];
  1594              elem.keydown(function (evt) {
  1595                  if (evt.ctrlKey) {
  1596                      return;
  1597                  }
  1598                  // This is so shift-enter can be caught to run a rule when tsTab is called from
  1599                  // the rule page
  1600                  if (evt.keyCode == 13 && evt.shiftKey) {
  1601                      return;
  1602                  }
  1603                  switch (evt.keyCode) {
  1604                      case 9: // tab
  1605                          evt.preventDefault();
  1606                          var v = ta.value;
  1607                          var start = ta.selectionStart;
  1608                          ta.value = v.substr(0, start) + "\t" + v.substr(start);
  1609                          ta.selectionStart = ta.selectionEnd = start + 1;
  1610                          return;
  1611                      case 13: // enter
  1612                          if (ta.selectionStart != ta.selectionEnd) {
  1613                              return;
  1614                          }
  1615                          evt.preventDefault();
  1616                          var v = ta.value;
  1617                          var start = ta.selectionStart;
  1618                          var sub = v.substr(0, start);
  1619                          var last = sub.lastIndexOf("\n") + 1;
  1620                          for (var i = last; i < sub.length && /[ \t]/.test(sub[i]); i++)
  1621                              ;
  1622                          var ws = sub.substr(last, i - last);
  1623                          ta.value = v.substr(0, start) + "\n" + ws + v.substr(start);
  1624                          ta.selectionStart = ta.selectionEnd = start + 1 + ws.length;
  1625                  }
  1626              });
  1627          },
  1628      };
  1629  });
  1630  bosunApp.directive('tsresizable', function () {
  1631      return {
  1632          restrict: 'A',
  1633          scope: {
  1634              callback: '&onResize'
  1635          },
  1636          link: function postLink(scope, elem, attrs) {
  1637              elem.resizable();
  1638              elem.on('resizestop', function (evt, ui) {
  1639                  if (scope.callback) {
  1640                      scope.callback();
  1641                  }
  1642              });
  1643          }
  1644      };
  1645  });
  1646  bosunApp.directive('tsTableSort', ['$timeout', function ($timeout) {
  1647          return {
  1648              link: function (scope, elem, attrs) {
  1649                  $timeout(function () {
  1650                      $(elem).tablesorter({
  1651                          sortList: scope.$eval(attrs.tsTableSort),
  1652                      });
  1653                  });
  1654              },
  1655          };
  1656      }]);
  1657  // https://gist.github.com/mlynch/dd407b93ed288d499778
  1658  bosunApp.directive('autofocus', ['$timeout', function ($timeout) {
  1659          return {
  1660              restrict: 'A',
  1661              link: function ($scope, $element) {
  1662                  $timeout(function () {
  1663                      $element[0].focus();
  1664                  });
  1665              }
  1666          };
  1667      }]);
  1668  bosunApp.directive('tsTimeLine', function () {
  1669      var tsdbFormat = d3.time.format.utc("%Y/%m/%d-%X");
  1670      function parseDate(s) {
  1671          return moment.utc(s).toDate();
  1672      }
  1673      var margin = {
  1674          top: 10,
  1675          right: 10,
  1676          bottom: 30,
  1677          left: 250,
  1678      };
  1679      return {
  1680          link: function (scope, elem, attrs) {
  1681              scope.shown = {};
  1682              scope.collapse = function (i, entry, v) {
  1683                  scope.shown[i] = !scope.shown[i];
  1684                  if (scope.loadTimelinePanel && entry && scope.shown[i]) {
  1685                      scope.loadTimelinePanel(entry, v);
  1686                  }
  1687              };
  1688              scope.$watch('alert_history', update);
  1689              function update(history) {
  1690                  if (!history) {
  1691                      return;
  1692                  }
  1693                  var entries = d3.entries(history);
  1694                  if (!entries.length) {
  1695                      return;
  1696                  }
  1697                  entries.sort(function (a, b) {
  1698                      return a.key.localeCompare(b.key);
  1699                  });
  1700                  scope.entries = entries;
  1701                  var values = entries.map(function (v) { return v.value; });
  1702                  var keys = entries.map(function (v) { return v.key; });
  1703                  var barheight = 500 / values.length;
  1704                  barheight = Math.min(barheight, 45);
  1705                  barheight = Math.max(barheight, 15);
  1706                  var svgHeight = values.length * barheight + margin.top + margin.bottom;
  1707                  var height = svgHeight - margin.top - margin.bottom;
  1708                  var svgWidth = elem.width();
  1709                  var width = svgWidth - margin.left - margin.right;
  1710                  var xScale = d3.time.scale.utc().range([0, width]);
  1711                  var xAxis = d3.svg.axis()
  1712                      .scale(xScale)
  1713                      .orient('bottom');
  1714                  elem.empty();
  1715                  var svg = d3.select(elem[0])
  1716                      .append('svg')
  1717                      .attr('width', svgWidth)
  1718                      .attr('height', svgHeight)
  1719                      .append('g')
  1720                      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1721                  svg.append('g')
  1722                      .attr('class', 'x axis tl-axis')
  1723                      .attr('transform', 'translate(0,' + height + ')');
  1724                  xScale.domain([
  1725                      d3.min(values, function (d) { return d3.min(d.History, function (c) { return parseDate(c.Time); }); }),
  1726                      d3.max(values, function (d) { return d3.max(d.History, function (c) { return parseDate(c.EndTime); }); }),
  1727                  ]);
  1728                  var legend = d3.select(elem[0])
  1729                      .append('div')
  1730                      .attr('class', 'tl-legend');
  1731                  var time_legend = legend
  1732                      .append('div')
  1733                      .text(values[0].History[0].Time);
  1734                  var alert_legend = legend
  1735                      .append('div')
  1736                      .text(keys[0]);
  1737                  svg.select('.x.axis')
  1738                      .transition()
  1739                      .call(xAxis);
  1740                  var chart = svg.append('g');
  1741                  angular.forEach(entries, function (entry, i) {
  1742                      chart.selectAll('.bars')
  1743                          .data(entry.value.History)
  1744                          .enter()
  1745                          .append('rect')
  1746                          .attr('class', function (d) { return 'tl-' + d.Status; })
  1747                          .attr('x', function (d) { return xScale(parseDate(d.Time)); })
  1748                          .attr('y', i * barheight)
  1749                          .attr('height', barheight)
  1750                          .attr('width', function (d) {
  1751                          return xScale(parseDate(d.EndTime)) - xScale(parseDate(d.Time));
  1752                      })
  1753                          .on('mousemove.x', mousemove_x)
  1754                          .on('mousemove.y', function (d) {
  1755                          alert_legend.text(entry.key);
  1756                      })
  1757                          .on('click', function (d, j) {
  1758                          var id = 'panel' + i + '-' + j;
  1759                          scope.shown['group' + i] = true;
  1760                          scope.shown[id] = true;
  1761                          if (scope.loadTimelinePanel) {
  1762                              scope.loadTimelinePanel(entry, d);
  1763                          }
  1764                          scope.$apply();
  1765                          setTimeout(function () {
  1766                              var e = $("#" + id);
  1767                              if (!e) {
  1768                                  console.log('no', id, e);
  1769                                  return;
  1770                              }
  1771                              $('html, body').scrollTop(e.offset().top);
  1772                          });
  1773                      });
  1774                  });
  1775                  chart.selectAll('.labels')
  1776                      .data(keys)
  1777                      .enter()
  1778                      .append('text')
  1779                      .attr('text-anchor', 'end')
  1780                      .attr('x', 0)
  1781                      .attr('dx', '-.5em')
  1782                      .attr('dy', '.25em')
  1783                      .attr('y', function (d, i) { return (i + .5) * barheight; })
  1784                      .text(function (d) { return d; });
  1785                  chart.selectAll('.sep')
  1786                      .data(values)
  1787                      .enter()
  1788                      .append('rect')
  1789                      .attr('y', function (d, i) { return (i + 1) * barheight; })
  1790                      .attr('height', 1)
  1791                      .attr('x', 0)
  1792                      .attr('width', width)
  1793                      .on('mousemove.x', mousemove_x);
  1794                  function mousemove_x() {
  1795                      var x = xScale.invert(d3.mouse(this)[0]);
  1796                      time_legend
  1797                          .text(tsdbFormat(x));
  1798                  }
  1799              }
  1800              ;
  1801          },
  1802      };
  1803  });
  1804  var fmtUnits = ['', 'k', 'M', 'G', 'T', 'P', 'E'];
  1805  function nfmt(s, mult, suffix, opts) {
  1806      opts = opts || {};
  1807      var n = parseFloat(s);
  1808      if (isNaN(n) && typeof s === 'string') {
  1809          return s;
  1810      }
  1811      if (opts.round)
  1812          n = Math.round(n);
  1813      if (!n)
  1814          return suffix ? '0 ' + suffix : '0';
  1815      if (isNaN(n) || !isFinite(n))
  1816          return '-';
  1817      var a = Math.abs(n);
  1818      if (a >= 1) {
  1819          var number = Math.floor(Math.log(a) / Math.log(mult));
  1820          a /= Math.pow(mult, Math.floor(number));
  1821          if (fmtUnits[number]) {
  1822              suffix = fmtUnits[number] + suffix;
  1823          }
  1824      }
  1825      var r = a.toFixed(5);
  1826      if (a < 1e-5) {
  1827          r = a.toString();
  1828      }
  1829      var neg = n < 0 ? '-' : '';
  1830      return neg + (+r) + suffix;
  1831  }
  1832  bosunApp.filter('nfmt', function () {
  1833      return function (s) {
  1834          return nfmt(s, 1000, '', {});
  1835      };
  1836  });
  1837  bosunApp.filter('bytes', function () {
  1838      return function (s) {
  1839          return nfmt(s, 1024, 'B', { round: true });
  1840      };
  1841  });
  1842  bosunApp.filter('bits', function () {
  1843      return function (s) {
  1844          return nfmt(s, 1024, 'b', { round: true });
  1845      };
  1846  });
  1847  bosunApp.directive('elastic', [
  1848      '$timeout',
  1849      function ($timeout) {
  1850          return {
  1851              restrict: 'A',
  1852              link: function ($scope, element) {
  1853                  $scope.initialHeight = $scope.initialHeight || element[0].style.height;
  1854                  var resize = function () {
  1855                      element[0].style.height = $scope.initialHeight;
  1856                      element[0].style.height = "" + element[0].scrollHeight + "px";
  1857                  };
  1858                  element.on("input change", resize);
  1859                  $timeout(resize, 0);
  1860              }
  1861          };
  1862      }
  1863  ]);
  1864  bosunApp.directive('tsBar', ['$window', 'nfmtFilter', function ($window, fmtfilter) {
  1865          var margin = {
  1866              top: 20,
  1867              right: 20,
  1868              bottom: 0,
  1869              left: 200,
  1870          };
  1871          return {
  1872              scope: {
  1873                  data: '=',
  1874                  height: '=',
  1875              },
  1876              link: function (scope, elem, attrs) {
  1877                  var svgHeight = +scope.height || 150;
  1878                  var height = svgHeight - margin.top - margin.bottom;
  1879                  var svgWidth;
  1880                  var width;
  1881                  var xScale = d3.scale.linear();
  1882                  var yScale = d3.scale.ordinal();
  1883                  var top = d3.select(elem[0])
  1884                      .append('svg')
  1885                      .attr('height', svgHeight)
  1886                      .attr('width', '100%');
  1887                  var svg = top
  1888                      .append('g');
  1889                  //.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1890                  var xAxis = d3.svg.axis()
  1891                      .scale(xScale)
  1892                      .orient("top");
  1893                  var yAxis = d3.svg.axis()
  1894                      .scale(yScale)
  1895                      .orient("left");
  1896                  scope.$watch('data', update);
  1897                  var w = angular.element($window);
  1898                  scope.$watch(function () {
  1899                      return w.width();
  1900                  }, resize, true);
  1901                  w.bind('resize', function () {
  1902                      scope.$apply();
  1903                  });
  1904                  function resize() {
  1905                      if (!scope.data) {
  1906                          return;
  1907                      }
  1908                      svgWidth = elem.width();
  1909                      if (svgWidth <= 0) {
  1910                          return;
  1911                      }
  1912                      margin.left = d3.max(scope.data, function (d) { return d.name.length * 8; });
  1913                      width = svgWidth - margin.left - margin.right;
  1914                      svgHeight = scope.data.length * 15;
  1915                      height = svgHeight - margin.top - margin.bottom;
  1916                      xScale.range([0, width]);
  1917                      yScale.rangeRoundBands([0, height], .1);
  1918                      yAxis.scale(yScale);
  1919                      svg.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  1920                      svg.attr('width', svgWidth);
  1921                      svg.attr('height', height);
  1922                      top.attr('height', svgHeight);
  1923                      xAxis.ticks(width / 60);
  1924                      draw();
  1925                  }
  1926                  function update(v) {
  1927                      if (!angular.isArray(v) || v.length == 0) {
  1928                          return;
  1929                      }
  1930                      resize();
  1931                  }
  1932                  function draw() {
  1933                      if (!scope.data) {
  1934                          return;
  1935                      }
  1936                      yScale.domain(scope.data.map(function (d) { return d.name; }));
  1937                      xScale.domain([0, d3.max(scope.data, function (d) { return d.Value; })]);
  1938                      svg.selectAll('g.axis').remove();
  1939                      //X axis
  1940                      svg.append("g")
  1941                          .attr("class", "x axis")
  1942                          .call(xAxis);
  1943                      svg.append("g")
  1944                          .attr("class", "y axis")
  1945                          .call(yAxis)
  1946                          .selectAll("text")
  1947                          .style("text-anchor", "end");
  1948                      var bars = svg.selectAll(".bar").data(scope.data);
  1949                      bars.enter()
  1950                          .append("rect")
  1951                          .attr("class", "bar")
  1952                          .attr("y", function (d) { return yScale(d.name); })
  1953                          .attr("height", yScale.rangeBand())
  1954                          .attr('width', function (d) { return xScale(d.Value); });
  1955                  }
  1956                  ;
  1957              },
  1958          };
  1959      }]);
  1960  bosunApp.directive('tsGraph', ['$window', 'nfmtFilter', function ($window, fmtfilter) {
  1961          var margin = {
  1962              top: 10,
  1963              right: 10,
  1964              bottom: 30,
  1965              left: 80,
  1966          };
  1967          return {
  1968              scope: {
  1969                  data: '=',
  1970                  annotations: '=',
  1971                  height: '=',
  1972                  generator: '=',
  1973                  brushStart: '=bstart',
  1974                  brushEnd: '=bend',
  1975                  enableBrush: '@',
  1976                  max: '=',
  1977                  min: '=',
  1978                  normalize: '=',
  1979                  annotation: '=',
  1980                  annotateEnabled: '=',
  1981                  showAnnotations: '=',
  1982              },
  1983              template: '<div class="row"></div>' + // chartElemt
  1984                  '<div class="row col-lg-12"></div>' + // timeElem
  1985                  '<div class"row">' + // legendAnnContainer
  1986                  '<div class="col-lg-6"></div>' + // legendElem
  1987                  '<div class="col-lg-6"></div>' + // annElem
  1988                  '</div>',
  1989              link: function (scope, elem, attrs, $compile) {
  1990                  var chartElem = d3.select(elem.children()[0]);
  1991                  var timeElem = d3.select(elem.children()[1]);
  1992                  var legendAnnContainer = angular.element(elem.children()[2]);
  1993                  var legendElem = d3.select(legendAnnContainer.children()[0]);
  1994                  if (scope.annotateEnabled) {
  1995                      var annElem = d3.select(legendAnnContainer.children()[1]);
  1996                  }
  1997                  var valueIdx = 1;
  1998                  if (scope.normalize) {
  1999                      valueIdx = 2;
  2000                  }
  2001                  var svgHeight = +scope.height || 150;
  2002                  var height = svgHeight - margin.top - margin.bottom;
  2003                  var svgWidth;
  2004                  var width;
  2005                  var yScale = d3.scale.linear().range([height, 0]);
  2006                  var xScale = d3.time.scale.utc();
  2007                  var xAxis = d3.svg.axis()
  2008                      .orient('bottom');
  2009                  var yAxis = d3.svg.axis()
  2010                      .scale(yScale)
  2011                      .orient('left')
  2012                      .ticks(Math.min(10, height / 20))
  2013                      .tickFormat(fmtfilter);
  2014                  var line;
  2015                  switch (scope.generator) {
  2016                      case 'area':
  2017                          line = d3.svg.area();
  2018                          break;
  2019                      default:
  2020                          line = d3.svg.line();
  2021                  }
  2022                  var brush = d3.svg.brush()
  2023                      .x(xScale)
  2024                      .on('brush', brushed)
  2025                      .on('brushend', annotateBrushed);
  2026                  var top = chartElem
  2027                      .append('svg')
  2028                      .attr('height', svgHeight)
  2029                      .attr('width', '100%');
  2030                  var svg = top
  2031                      .append('g')
  2032                      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
  2033                  var defs = svg.append('defs')
  2034                      .append('clipPath')
  2035                      .attr('id', 'clip')
  2036                      .append('rect')
  2037                      .attr('height', height);
  2038                  var chart = svg.append('g')
  2039                      .attr('pointer-events', 'all')
  2040                      .attr('clip-path', 'url(#clip)');
  2041                  svg.append('g')
  2042                      .attr('class', 'x axis')
  2043                      .attr('transform', 'translate(0,' + height + ')');
  2044                  svg.append('g')
  2045                      .attr('class', 'y axis');
  2046                  var paths = chart.append('g');
  2047                  chart.append('g')
  2048                      .attr('class', 'x brush');
  2049                  if (scope.annotateEnabled) {
  2050                      var ann = chart.append('g');
  2051                  }
  2052                  top.append('rect')
  2053                      .style('opacity', 0)
  2054                      .attr('x', 0)
  2055                      .attr('y', 0)
  2056                      .attr('height', height)
  2057                      .attr('width', margin.left)
  2058                      .style('cursor', 'pointer')
  2059                      .on('click', yaxisToggle);
  2060                  var xloc = timeElem.append('div').attr("class", "col-lg-6");
  2061                  xloc.style('float', 'left');
  2062                  var brushText = timeElem.append('div').attr("class", "col-lg-6").append('p').attr("class", "text-right");
  2063                  var legend = legendElem;
  2064                  var aLegend = annElem;
  2065                  var color = d3.scale.ordinal().range([
  2066                      '#e41a1c',
  2067                      '#377eb8',
  2068                      '#4daf4a',
  2069                      '#984ea3',
  2070                      '#ff7f00',
  2071                      '#a65628',
  2072                      '#f781bf',
  2073                      '#999999',
  2074                  ]);
  2075                  var annColor = d3.scale.ordinal().range([
  2076                      '#e41a1c',
  2077                      '#377eb8',
  2078                      '#4daf4a',
  2079                      '#984ea3',
  2080                      '#ff7f00',
  2081                      '#a65628',
  2082                      '#f781bf',
  2083                      '#999999',
  2084                  ]);
  2085                  var mousex = 0;
  2086                  var mousey = 0;
  2087                  var oldx = 0;
  2088                  var hover = svg.append('g')
  2089                      .attr('class', 'hover')
  2090                      .style('pointer-events', 'none')
  2091                      .style('display', 'none');
  2092                  var hoverPoint = hover.append('svg:circle')
  2093                      .attr('r', 5);
  2094                  var hoverRect = hover.append('svg:rect')
  2095                      .attr('fill', 'white');
  2096                  var hoverText = hover.append('svg:text')
  2097                      .style('font-size', '12px');
  2098                  var focus = svg.append('g')
  2099                      .attr('class', 'focus')
  2100                      .style('pointer-events', 'none');
  2101                  focus.append('line');
  2102                  var yaxisZero = false;
  2103                  function yaxisToggle() {
  2104                      yaxisZero = !yaxisZero;
  2105                      draw();
  2106                  }
  2107                  var drawAnnLegend = function () {
  2108                      if (scope.annotation) {
  2109                          aLegend.html('');
  2110                          var a = scope.annotation;
  2111                          //var table = aLegend.append('table').attr("class", "table table-condensed")
  2112                          var table = aLegend.append("div");
  2113                          var row = table.append("div").attr("class", "row");
  2114                          row.append("div").attr("class", "col-lg-2").text("CreationUser");
  2115                          row.append("div").attr("class", "col-lg-10").text(a.CreationUser);
  2116                          row = table.append("div").attr("class", "row");
  2117                          row.append("div").attr("class", "col-lg-2").text("Owner");
  2118                          row.append("div").attr("class", "col-lg-10").text(a.Owner);
  2119                          row = table.append("div").attr("class", "row");
  2120                          row.append("div").attr("class", "col-lg-2").text("Url");
  2121                          row.append("div").attr("class", "col-lg-10").append('a')
  2122                              .attr("xlink:href", a.Url).text(a.Url).on("click", function (d) {
  2123                              window.open(a.Url, "_blank");
  2124                          });
  2125                          row = table.append("div").attr("class", "row");
  2126                          row.append("div").attr("class", "col-lg-2").text("Category");
  2127                          row.append("div").attr("class", "col-lg-10").text(a.Category);
  2128                          row = table.append("div").attr("class", "row");
  2129                          row.append("div").attr("class", "col-lg-2").text("Host");
  2130                          row.append("div").attr("class", "col-lg-10").text(a.Host);
  2131                          row = table.append("div").attr("class", "row");
  2132                          row.append("div").attr("class", "col-lg-2").text("Message");
  2133                          row.append("div").attr("class", "col-lg-10").text(a.Message);
  2134                      } //
  2135                  };
  2136                  var drawLegend = _.throttle(function (normalizeIdx) {
  2137                      var names = legend.selectAll('.series')
  2138                          .data(scope.data, function (d) { return d.Name; });
  2139                      names.enter()
  2140                          .append('div')
  2141                          .attr('class', 'series');
  2142                      names.exit()
  2143                          .remove();
  2144                      var xi = xScale.invert(mousex);
  2145                      xloc.text('Time: ' + fmtTime(xi));
  2146                      var t = xi.getTime() / 1000;
  2147                      var minDist = width + height;
  2148                      var minName, minColor;
  2149                      var minX, minY;
  2150                      names
  2151                          .each(function (d) {
  2152                          var idx = bisect(d.Data, t);
  2153                          if (idx >= d.Data.length) {
  2154                              idx = d.Data.length - 1;
  2155                          }
  2156                          var e = d3.select(this);
  2157                          var pt = d.Data[idx];
  2158                          if (pt) {
  2159                              e.attr('title', pt[normalizeIdx]);
  2160                              e.text(d.Name + ': ' + fmtfilter(pt[1]));
  2161                              var ptx = xScale(pt[0] * 1000);
  2162                              var pty = yScale(pt[normalizeIdx]);
  2163                              var ptd = Math.sqrt(Math.pow(ptx - mousex, 2) +
  2164                                  Math.pow(pty - mousey, 2));
  2165                              if (ptd < minDist) {
  2166                                  minDist = ptd;
  2167                                  minX = ptx;
  2168                                  minY = pty;
  2169                                  minName = d.Name + ': ' + pt[1];
  2170                                  minColor = color(d.Name);
  2171                              }
  2172                          }
  2173                      })
  2174                          .style('color', function (d) { return color(d.Name); });
  2175                      hover
  2176                          .attr('transform', 'translate(' + minX + ',' + minY + ')');
  2177                      hoverPoint.style('fill', minColor);
  2178                      hoverText
  2179                          .text(minName)
  2180                          .style('fill', minColor);
  2181                      var isRight = minX > width / 2;
  2182                      var isBottom = minY > height / 2;
  2183                      hoverText
  2184                          .attr('x', isRight ? -5 : 5)
  2185                          .attr('y', isBottom ? -8 : 15)
  2186                          .attr('text-anchor', isRight ? 'end' : 'start');
  2187                      var node = hoverText.node();
  2188                      var bb = node.getBBox();
  2189                      hoverRect
  2190                          .attr('x', bb.x - 1)
  2191                          .attr('y', bb.y - 1)
  2192                          .attr('height', bb.height + 2)
  2193                          .attr('width', bb.width + 2);
  2194                      var x = mousex;
  2195                      if (x > width) {
  2196                          x = 0;
  2197                      }
  2198                      focus.select('line')
  2199                          .attr('x1', x)
  2200                          .attr('x2', x)
  2201                          .attr('y1', 0)
  2202                          .attr('y2', height);
  2203                      if (extentStart) {
  2204                          var s = extentStart;
  2205                          if (extentEnd != extentStart) {
  2206                              s += ' - ' + extentEnd;
  2207                              s += ' (' + extentDiff + ')';
  2208                          }
  2209                          brushText.text(s);
  2210                      }
  2211                  }, 50);
  2212                  scope.$watchCollection('[data, annotations, showAnnotations]', update);
  2213                  var showAnnotations = function (show) {
  2214                      if (show) {
  2215                          ann.attr("visibility", "visible");
  2216                          return;
  2217                      }
  2218                      ann.attr("visibility", "hidden");
  2219                      aLegend.html('');
  2220                  };
  2221                  var w = angular.element($window);
  2222                  scope.$watch(function () {
  2223                      return w.width();
  2224                  }, resize, true);
  2225                  w.bind('resize', function () {
  2226                      scope.$apply();
  2227                  });
  2228                  function resize() {
  2229                      svgWidth = elem.width();
  2230                      if (svgWidth <= 0) {
  2231                          return;
  2232                      }
  2233                      width = svgWidth - margin.left - margin.right;
  2234                      xScale.range([0, width]);
  2235                      xAxis.scale(xScale);
  2236                      if (!mousex) {
  2237                          mousex = width + 1;
  2238                      }
  2239                      svg.attr('width', svgWidth);
  2240                      defs.attr('width', width);
  2241                      xAxis.ticks(width / 60);
  2242                      draw();
  2243                  }
  2244                  var oldx = 0;
  2245                  var bisect = d3.bisector(function (d) { return d[0]; }).left;
  2246                  var bisectA = d3.bisector(function (d) { return moment(d.StartDate).unix(); }).left;
  2247                  function update(v) {
  2248                      if (!angular.isArray(v) || v.length == 0) {
  2249                          return;
  2250                      }
  2251                      d3.selectAll(".x.brush").call(brush.clear());
  2252                      if (scope.annotateEnabled) {
  2253                          showAnnotations(scope.showAnnotations);
  2254                      }
  2255                      resize();
  2256                  }
  2257                  function draw() {
  2258                      if (!scope.data) {
  2259                          return;
  2260                      }
  2261                      if (scope.normalize) {
  2262                          valueIdx = 2;
  2263                      }
  2264                      function mousemove() {
  2265                          var pt = d3.mouse(this);
  2266                          mousex = pt[0];
  2267                          mousey = pt[1];
  2268                          drawLegend(valueIdx);
  2269                      }
  2270                      scope.data.map(function (data, i) {
  2271                          var max = d3.max(data.Data, function (d) { return d[1]; });
  2272                          data.Data.map(function (d, j) {
  2273                              d.push(d[1] / max * 100 || 0);
  2274                          });
  2275                      });
  2276                      line.y(function (d) { return yScale(d[valueIdx]); });
  2277                      line.x(function (d) { return xScale(d[0] * 1000); });
  2278                      var xdomain = [
  2279                          d3.min(scope.data, function (d) { return d3.min(d.Data, function (c) { return c[0]; }); }) * 1000,
  2280                          d3.max(scope.data, function (d) { return d3.max(d.Data, function (c) { return c[0]; }); }) * 1000,
  2281                      ];
  2282                      if (!oldx) {
  2283                          oldx = xdomain[1];
  2284                      }
  2285                      xScale.domain(xdomain);
  2286                      var ymin = d3.min(scope.data, function (d) { return d3.min(d.Data, function (c) { return c[1]; }); });
  2287                      var ymax = d3.max(scope.data, function (d) { return d3.max(d.Data, function (c) { return c[valueIdx]; }); });
  2288                      var diff = (ymax - ymin) / 50;
  2289                      if (!diff) {
  2290                          diff = 1;
  2291                      }
  2292                      ymin -= diff;
  2293                      ymax += diff;
  2294                      if (yaxisZero) {
  2295                          if (ymin > 0) {
  2296                              ymin = 0;
  2297                          }
  2298                          else if (ymax < 0) {
  2299                              ymax = 0;
  2300                          }
  2301                      }
  2302                      var ydomain = [ymin, ymax];
  2303                      if (angular.isNumber(scope.min)) {
  2304                          ydomain[0] = +scope.min;
  2305                      }
  2306                      if (angular.isNumber(scope.max)) {
  2307                          ydomain[valueIdx] = +scope.max;
  2308                      }
  2309                      yScale.domain(ydomain);
  2310                      if (scope.generator == 'area') {
  2311                          line.y0(yScale(0));
  2312                      }
  2313                      svg.select('.x.axis')
  2314                          .transition()
  2315                          .call(xAxis);
  2316                      svg.select('.y.axis')
  2317                          .transition()
  2318                          .call(yAxis);
  2319                      svg.append('text')
  2320                          .attr("class", "ylabel")
  2321                          .attr("transform", "rotate(-90)")
  2322                          .attr("y", -margin.left)
  2323                          .attr("x", -(height / 2))
  2324                          .attr("dy", "1em")
  2325                          .text(_.uniq(scope.data.map(function (v) { return v.Unit; })).join("; "));
  2326                      if (scope.annotateEnabled) {
  2327                          var rowId = {}; // annotation Id -> rowId
  2328                          var rowEndDate = {}; // rowId -> EndDate
  2329                          var maxRow = 0;
  2330                          for (var i = 0; i < scope.annotations.length; i++) {
  2331                              if (i == 0) {
  2332                                  rowId[scope.annotations[i].Id] = 0;
  2333                                  rowEndDate[0] = scope.annotations[0].EndDate;
  2334                                  continue;
  2335                              }
  2336                              for (var row = 0; row <= maxRow + 1; row++) {
  2337                                  if (row == maxRow + 1) {
  2338                                      rowId[scope.annotations[i].Id] = row;
  2339                                      rowEndDate[row] = scope.annotations[i].EndDate;
  2340                                      maxRow += 1;
  2341                                      break;
  2342                                  }
  2343                                  if (rowEndDate[row] < scope.annotations[i].StartDate) {
  2344                                      rowId[scope.annotations[i].Id] = row;
  2345                                      rowEndDate[row] = scope.annotations[i].EndDate;
  2346                                      break;
  2347                                  }
  2348                              }
  2349                          }
  2350                          var annotations = ann.selectAll('.annotation')
  2351                              .data(scope.annotations, function (d) { return d.Id; });
  2352                          annotations.enter()
  2353                              .append("svg:a")
  2354                              .append('rect')
  2355                              .attr('visilibity', function () {
  2356                              if (scope.showAnnotations) {
  2357                                  return "visible";
  2358                              }
  2359                              return "hidden";
  2360                          })
  2361                              .attr("y", function (d) { return rowId[d.Id] * ((height * .05) + 2); })
  2362                              .attr("height", height * .05)
  2363                              .attr("class", "annotation")
  2364                              .attr("stroke", function (d) { return annColor(d.Id); })
  2365                              .attr("stroke-opacity", .5)
  2366                              .attr("fill", function (d) { return annColor(d.Id); })
  2367                              .attr("fill-opacity", 0.1)
  2368                              .attr("stroke-width", 1)
  2369                              .attr("x", function (d) { return xScale(moment(d.StartDate).utc().unix() * 1000); })
  2370                              .attr("width", function (d) {
  2371                              var startT = moment(d.StartDate).utc().unix() * 1000;
  2372                              var endT = moment(d.EndDate).utc().unix() * 1000;
  2373                              var calcWidth = xScale(endT) - xScale(startT);
  2374                              // Never render boxes with less than 8 pixels are they are difficult to click
  2375                              if (calcWidth < 8) {
  2376                                  return 8;
  2377                              }
  2378                              return calcWidth;
  2379                          })
  2380                              .on("mouseenter", function (ann) {
  2381                              if (!scope.showAnnotations) {
  2382                                  return;
  2383                              }
  2384                              if (ann) {
  2385                                  scope.annotation = ann;
  2386                                  drawAnnLegend();
  2387                              }
  2388                              scope.$apply();
  2389                          })
  2390                              .on("click", function () {
  2391                              if (!scope.showAnnotations) {
  2392                                  return;
  2393                              }
  2394                              angular.element('#modalShower').trigger('click');
  2395                          });
  2396                          annotations.exit().remove();
  2397                      }
  2398                      var queries = paths.selectAll('.line')
  2399                          .data(scope.data, function (d) { return d.Name; });
  2400                      switch (scope.generator) {
  2401                          case 'area':
  2402                              queries.enter()
  2403                                  .append('path')
  2404                                  .attr('stroke', function (d) { return color(d.Name); })
  2405                                  .attr('class', 'line')
  2406                                  .style('fill', function (d) { return color(d.Name); });
  2407                              break;
  2408                          default:
  2409                              queries.enter()
  2410                                  .append('path')
  2411                                  .attr('stroke', function (d) { return color(d.Name); })
  2412                                  .attr('class', 'line');
  2413                      }
  2414                      queries.exit()
  2415                          .remove();
  2416                      queries
  2417                          .attr('d', function (d) { return line(d.Data); })
  2418                          .attr('transform', null)
  2419                          .transition()
  2420                          .ease('linear')
  2421                          .attr('transform', 'translate(' + (xScale(oldx) - xScale(xdomain[1])) + ')');
  2422                      chart.select('.x.brush')
  2423                          .call(brush)
  2424                          .selectAll('rect')
  2425                          .attr('height', height)
  2426                          .on('mouseover', function () {
  2427                          hover.style('display', 'block');
  2428                      })
  2429                          .on('mouseout', function () {
  2430                          hover.style('display', 'none');
  2431                      })
  2432                          .on('mousemove', mousemove);
  2433                      chart.select('.x.brush .extent')
  2434                          .style('stroke', '#fff')
  2435                          .style('fill-opacity', '.125')
  2436                          .style('shape-rendering', 'crispEdges');
  2437                      oldx = xdomain[1];
  2438                      drawLegend(valueIdx);
  2439                  }
  2440                  ;
  2441                  var extentStart;
  2442                  var extentEnd;
  2443                  var extentDiff;
  2444                  function brushed() {
  2445                      var e;
  2446                      e = d3.event.sourceEvent;
  2447                      if (e.shiftKey) {
  2448                          return;
  2449                      }
  2450                      var extent = brush.extent();
  2451                      extentStart = datefmt(extent[0]);
  2452                      extentEnd = datefmt(extent[1]);
  2453                      extentDiff = fmtDuration(moment(extent[1]).diff(moment(extent[0])));
  2454                      drawLegend(valueIdx);
  2455                      if (scope.enableBrush && extentEnd != extentStart) {
  2456                          scope.brushStart = extentStart;
  2457                          scope.brushEnd = extentEnd;
  2458                          scope.$apply();
  2459                      }
  2460                  }
  2461                  function annotateBrushed() {
  2462                      if (!scope.annotateEnabled) {
  2463                          return;
  2464                      }
  2465                      var e;
  2466                      e = d3.event.sourceEvent;
  2467                      if (!e.shiftKey) {
  2468                          return;
  2469                      }
  2470                      var extent = brush.extent();
  2471                      scope.annotation = new Annotation();
  2472                      scope.annotation.StartDate = moment(extent[0]).utc().format(timeFormat);
  2473                      scope.annotation.EndDate = moment(extent[1]).utc().format(timeFormat);
  2474                      scope.$apply(); // This logs a console type error, but also works .. odd.
  2475                      angular.element('#modalShower').trigger('click');
  2476                  }
  2477                  var mfmt = 'YYYY/MM/DD-HH:mm:ss';
  2478                  function datefmt(d) {
  2479                      return moment(d).utc().format(mfmt);
  2480                  }
  2481              },
  2482          };
  2483      }]);
  2484  bosunControllers.controller('ErrorCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
  2485          $scope.loading = true;
  2486          $http.get('/api/errors')
  2487              .success(function (data) {
  2488              $scope.errors = [];
  2489              _(data).forEach(function (err, name) {
  2490                  err.Name = name;
  2491                  err.Sum = 0;
  2492                  err.Shown = true;
  2493                  _(err.Errors).forEach(function (line) {
  2494                      err.Sum += line.Count;
  2495                      line.FirstTime = moment.utc(line.FirstTime);
  2496                      line.LastTime = moment.utc(line.LastTime);
  2497                  });
  2498                  $scope.errors.push(err);
  2499              });
  2500          })
  2501              .error(function (data) {
  2502              $scope.error = "Error fetching data: " + data;
  2503          })
  2504              .finally(function () { $scope.loading = false; });
  2505          $scope.click = function (err, event) {
  2506              event.stopPropagation();
  2507          };
  2508          $scope.totalLines = function () {
  2509              return $scope.errors.length;
  2510          };
  2511          $scope.selectedLines = function () {
  2512              var t = 0;
  2513              _($scope.errors).forEach(function (err) {
  2514                  if (err.checked) {
  2515                      t++;
  2516                  }
  2517              });
  2518              return t;
  2519          };
  2520          var getChecked = function () {
  2521              var keys = [];
  2522              _($scope.errors).forEach(function (err) {
  2523                  if (err.checked) {
  2524                      keys.push(err.Name);
  2525                  }
  2526              });
  2527              return keys;
  2528          };
  2529          var clear = function (keys) {
  2530              $http.post('/api/errors', keys)
  2531                  .success(function (data) {
  2532                  $route.reload();
  2533              })
  2534                  .error(function (data) {
  2535                  $scope.error = "Error Clearing Errors: " + data;
  2536              });
  2537          };
  2538          $scope.clearAll = function () {
  2539              clear(["all"]);
  2540          };
  2541          $scope.clearSelected = function () {
  2542              var keys = getChecked();
  2543              clear(keys);
  2544          };
  2545          $scope.ruleLink = function (line, err) {
  2546              var url = "/config?alert=" + err.Name;
  2547              var fromDate = moment.utc(line.FirstTime);
  2548              url += "&fromDate=" + fromDate.format("YYYY-MM-DD");
  2549              url += "&fromTime=" + fromDate.format("hh:mm");
  2550              var toDate = moment.utc(line.LastTime);
  2551              url += "&toDate=" + toDate.format("YYYY-MM-DD");
  2552              url += "&toTime=" + toDate.format("hh:mm");
  2553              return url;
  2554          };
  2555      }]);
  2556  /// <reference path="0-bosun.ts" />
  2557  var TagSet = /** @class */ (function () {
  2558      function TagSet() {
  2559      }
  2560      return TagSet;
  2561  }());
  2562  var TagV = /** @class */ (function () {
  2563      function TagV() {
  2564      }
  2565      return TagV;
  2566  }());
  2567  var RateOptions = /** @class */ (function () {
  2568      function RateOptions() {
  2569      }
  2570      return RateOptions;
  2571  }());
  2572  var Filter = /** @class */ (function () {
  2573      function Filter(f) {
  2574          this.type = f && f.type || "auto";
  2575          this.tagk = f && f.tagk || "";
  2576          this.filter = f && f.filter || "";
  2577          this.groupBy = f && f.groupBy || false;
  2578      }
  2579      return Filter;
  2580  }());
  2581  var FilterMap = /** @class */ (function () {
  2582      function FilterMap() {
  2583      }
  2584      return FilterMap;
  2585  }());
  2586  var Query = /** @class */ (function () {
  2587      function Query(filterSupport, q) {
  2588          this.aggregator = q && q.aggregator || 'sum';
  2589          this.metric = q && q.metric || '';
  2590          this.rate = q && q.rate || false;
  2591          this.rateOptions = q && q.rateOptions || new RateOptions;
  2592          if (q && !q.derivative) {
  2593              // back compute derivative from q
  2594              if (!this.rate) {
  2595                  this.derivative = 'gauge';
  2596              }
  2597              else if (this.rateOptions.counter) {
  2598                  this.derivative = 'counter';
  2599              }
  2600              else {
  2601                  this.derivative = 'rate';
  2602              }
  2603          }
  2604          else {
  2605              this.derivative = q && q.derivative || 'auto';
  2606          }
  2607          this.ds = q && q.ds || '';
  2608          this.dstime = q && q.dstime || '';
  2609          this.tags = q && q.tags || new TagSet;
  2610          this.gbFilters = q && q.gbFilters || new FilterMap;
  2611          this.nGbFilters = q && q.nGbFilters || new FilterMap;
  2612          var that = this;
  2613          // Copy tags with values to group by filters so old links work
  2614          if (filterSupport) {
  2615              _.each(this.tags, function (v, k) {
  2616                  if (v === "") {
  2617                      return;
  2618                  }
  2619                  var f = new (Filter);
  2620                  f.filter = v;
  2621                  f.groupBy = true;
  2622                  f.tagk = k;
  2623                  that.gbFilters[k] = f;
  2624              });
  2625              // Load filters from raw query and turn them into gb and nGbFilters.
  2626              // This makes links from other pages work (i.e. the expr page)
  2627              if (_.has(q, 'filters')) {
  2628                  _.each(q.filters, function (filter) {
  2629                      if (filter.groupBy) {
  2630                          that.gbFilters[filter.tagk] = filter;
  2631                          return;
  2632                      }
  2633                      that.nGbFilters[filter.tagk] = filter;
  2634                  });
  2635              }
  2636          }
  2637          this.setFilters();
  2638          this.setDs();
  2639          this.setDerivative();
  2640      }
  2641      Query.prototype.setFilters = function () {
  2642          this.filters = [];
  2643          var that = this;
  2644          _.each(this.gbFilters, function (filter, tagk) {
  2645              if (filter.filter && filter.type) {
  2646                  that.filters.push(filter);
  2647              }
  2648          });
  2649          _.each(this.nGbFilters, function (filter, tagk) {
  2650              if (filter.filter && filter.type) {
  2651                  that.filters.push(filter);
  2652              }
  2653          });
  2654      };
  2655      Query.prototype.setDs = function () {
  2656          if (this.dstime && this.ds) {
  2657              this.downsample = this.dstime + '-' + this.ds;
  2658          }
  2659          else {
  2660              this.downsample = '';
  2661          }
  2662      };
  2663      Query.prototype.setDerivative = function () {
  2664          var max = this.rateOptions.counterMax;
  2665          this.rate = false;
  2666          this.rateOptions = new RateOptions();
  2667          switch (this.derivative) {
  2668              case "rate":
  2669                  this.rate = true;
  2670                  break;
  2671              case "counter":
  2672                  this.rate = true;
  2673                  this.rateOptions.counter = true;
  2674                  this.rateOptions.counterMax = max;
  2675                  this.rateOptions.resetValue = 1;
  2676                  break;
  2677              case "gauge":
  2678                  this.rate = false;
  2679                  break;
  2680          }
  2681      };
  2682      return Query;
  2683  }());
  2684  var GraphRequest = /** @class */ (function () {
  2685      function GraphRequest() {
  2686          this.start = '1h-ago';
  2687          this.queries = [];
  2688      }
  2689      GraphRequest.prototype.prune = function () {
  2690          var _this = this;
  2691          for (var i = 0; i < this.queries.length; i++) {
  2692              angular.forEach(this.queries[i], function (v, k) {
  2693                  var qi = _this.queries[i];
  2694                  switch (typeof v) {
  2695                      case "string":
  2696                          if (!v) {
  2697                              delete qi[k];
  2698                          }
  2699                          break;
  2700                      case "boolean":
  2701                          if (!v) {
  2702                              delete qi[k];
  2703                          }
  2704                          break;
  2705                      case "object":
  2706                          if (Object.keys(v).length == 0) {
  2707                              delete qi[k];
  2708                          }
  2709                          break;
  2710                  }
  2711              });
  2712          }
  2713      };
  2714      return GraphRequest;
  2715  }());
  2716  var graphRefresh;
  2717  var Version = /** @class */ (function () {
  2718      function Version() {
  2719      }
  2720      return Version;
  2721  }());
  2722  bosunControllers.controller('GraphCtrl', ['$scope', '$http', '$location', '$route', '$timeout', 'authService', function ($scope, $http, $location, $route, $timeout, auth) {
  2723          $scope.aggregators = ["sum", "min", "max", "avg", "dev", "zimsum", "mimmin", "mimmax"];
  2724          $scope.dsaggregators = ["", "sum", "min", "max", "avg", "dev", "zimsum", "mimmin", "mimmax"];
  2725          $scope.filters = ["auto", "iliteral_or", "iwildcard", "literal_or", "not_iliteral_or", "not_literal_or", "regexp", "wildcard"];
  2726          if ($scope.version.Major >= 2 && $scope.version.Minor >= 2) {
  2727              $scope.filterSupport = true;
  2728          }
  2729          $scope.rate_options = ["auto", "gauge", "counter", "rate"];
  2730          $scope.canAuto = {};
  2731          $scope.showAnnotations = (getShowAnnotations() == "true");
  2732          $scope.setShowAnnotations = function () {
  2733              if ($scope.showAnnotations) {
  2734                  setShowAnnotations("true");
  2735                  return;
  2736              }
  2737              setShowAnnotations("false");
  2738          };
  2739          var search = $location.search();
  2740          var j = search.json;
  2741          if (search.b64) {
  2742              j = atob(search.b64);
  2743          }
  2744          $scope.annotation = new Annotation();
  2745          var request = j ? JSON.parse(j) : new GraphRequest;
  2746          $scope.index = parseInt($location.hash()) || 0;
  2747          $scope.tagvs = [];
  2748          $scope.sorted_tagks = [];
  2749          $scope.query_p = [];
  2750          angular.forEach(request.queries, function (q, i) {
  2751              $scope.query_p[i] = new Query($scope.filterSupport, q);
  2752          });
  2753          $scope.start = request.start;
  2754          $scope.end = request.end;
  2755          $scope.autods = search.autods != 'false';
  2756          $scope.refresh = search.refresh == 'true';
  2757          $scope.normalize = search.normalize == 'true';
  2758          if (search.min) {
  2759              $scope.min = +search.min;
  2760          }
  2761          if (search.max) {
  2762              $scope.max = +search.max;
  2763          }
  2764          var duration_map = {
  2765              "s": "s",
  2766              "m": "m",
  2767              "h": "h",
  2768              "d": "d",
  2769              "w": "w",
  2770              "n": "M",
  2771              "y": "y",
  2772          };
  2773          var isRel = /^(\d+)(\w)-ago$/;
  2774          function RelToAbs(m) {
  2775              return moment().utc().subtract(parseFloat(m[1]), duration_map[m[2]]).format();
  2776          }
  2777          function AbsToRel(s) {
  2778              //Not strict parsing of the time format. For example, just "2014" will be valid
  2779              var t = moment.utc(s, moment.defaultFormat).fromNow();
  2780              return t;
  2781          }
  2782          function SwapTime(s) {
  2783              if (!s) {
  2784                  return moment().utc().format();
  2785              }
  2786              var m = isRel.exec(s);
  2787              if (m) {
  2788                  return RelToAbs(m);
  2789              }
  2790              return AbsToRel(s);
  2791          }
  2792          $scope.submitAnnotation = function () {
  2793              $scope.annotation.CreationUser = auth.GetUsername();
  2794              $http.post('/api/annotation', $scope.annotation)
  2795                  .success(function (data) {
  2796                  //debugger;
  2797                  if ($scope.annotation.Id == "" && $scope.annotation.Owner != "") {
  2798                      setOwner($scope.annotation.Owner);
  2799                  }
  2800                  $scope.annotation = new Annotation(data);
  2801                  $scope.error = "";
  2802                  // This seems to make angular refresh, where a push doesn't
  2803                  $scope.annotations = $scope.annotations.concat($scope.annotation);
  2804              })
  2805                  .error(function (error) {
  2806                  $scope.error = error;
  2807              });
  2808          };
  2809          $scope.deleteAnnotation = function () { return $http.delete('/api/annotation/' + $scope.annotation.Id)
  2810              .success(function (data) {
  2811              $scope.error = "";
  2812              $scope.annotations = _.without($scope.annotations, _.findWhere($scope.annotations, { Id: $scope.annotation.Id }));
  2813          })
  2814              .error(function (error) {
  2815              $scope.error = error;
  2816          }); };
  2817          $scope.SwitchTimes = function () {
  2818              $scope.start = SwapTime($scope.start);
  2819              $scope.end = SwapTime($scope.end);
  2820          };
  2821          $scope.AddTab = function () {
  2822              $scope.index = $scope.query_p.length;
  2823              $scope.query_p.push(new Query($scope.filterSupport));
  2824          };
  2825          $scope.setIndex = function (i) {
  2826              $scope.index = i;
  2827          };
  2828          var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
  2829          if ($scope.annotateEnabled) {
  2830              $http.get('/api/annotation/values/Owner')
  2831                  .success(function (data) {
  2832                  $scope.owners = data;
  2833              });
  2834              $http.get('/api/annotation/values/Category')
  2835                  .success(function (data) {
  2836                  $scope.categories = data;
  2837              });
  2838              $http.get('/api/annotation/values/Host')
  2839                  .success(function (data) {
  2840                  $scope.hosts = data;
  2841              });
  2842          }
  2843          $scope.GetTagKByMetric = function (index) {
  2844              $scope.tagvs[index] = new TagV;
  2845              var metric = $scope.query_p[index].metric;
  2846              if (!metric) {
  2847                  $scope.canAuto[metric] = true;
  2848                  return;
  2849              }
  2850              $http.get('/api/tagk/' + encodeURIComponent(metric))
  2851                  .success(function (data) {
  2852                  var q = $scope.query_p[index];
  2853                  var tags = new TagSet;
  2854                  q.metric_tags = {};
  2855                  if (!q.gbFilters) {
  2856                      q.gbFilters = new FilterMap;
  2857                  }
  2858                  if (!q.nGbFilters) {
  2859                      q.nGbFilters = new FilterMap;
  2860                  }
  2861                  for (var i = 0; i < data.length; i++) {
  2862                      var d = data[i];
  2863                      if ($scope.filterSupport) {
  2864                          if (!q.gbFilters[d]) {
  2865                              var filter = new Filter;
  2866                              filter.tagk = d;
  2867                              filter.groupBy = true;
  2868                              q.gbFilters[d] = filter;
  2869                          }
  2870                          if (!q.nGbFilters[d]) {
  2871                              var filter = new Filter;
  2872                              filter.tagk = d;
  2873                              q.nGbFilters[d] = filter;
  2874                          }
  2875                      }
  2876                      if (q.tags) {
  2877                          tags[d] = q.tags[d];
  2878                      }
  2879                      if (!tags[d]) {
  2880                          tags[d] = '';
  2881                      }
  2882                      q.metric_tags[d] = true;
  2883                      GetTagVs(d, index);
  2884                  }
  2885                  angular.forEach(q.tags, function (val, key) {
  2886                      if (val) {
  2887                          tags[key] = val;
  2888                      }
  2889                  });
  2890                  q.tags = tags;
  2891                  // Make sure host is always the first tag.
  2892                  $scope.sorted_tagks[index] = Object.keys(tags);
  2893                  $scope.sorted_tagks[index].sort(function (a, b) {
  2894                      if (a == 'host') {
  2895                          return -1;
  2896                      }
  2897                      else if (b == 'host') {
  2898                          return 1;
  2899                      }
  2900                      return a.localeCompare(b);
  2901                  });
  2902              })
  2903                  .error(function (error) {
  2904                  $scope.error = 'Unable to fetch metrics: ' + error;
  2905              });
  2906              $http.get('/api/metadata/metrics?metric=' + encodeURIComponent(metric))
  2907                  .success(function (data) {
  2908                  var canAuto = data && data.Rate;
  2909                  $scope.canAuto[metric] = canAuto;
  2910              })
  2911                  .error(function (err) {
  2912                  $scope.error = err;
  2913              });
  2914          };
  2915          if ($scope.query_p.length == 0) {
  2916              $scope.AddTab();
  2917          }
  2918          $http.get('/api/metric' + "?since=" + moment().utc().subtract(2, "days").unix())
  2919              .success(function (data) {
  2920              $scope.metrics = data;
  2921          })
  2922              .error(function (error) {
  2923              $scope.error = 'Unable to fetch metrics: ' + error;
  2924          });
  2925          function GetTagVs(k, index) {
  2926              $http.get('/api/tagv/' + encodeURIComponent(k) + '/' + $scope.query_p[index].metric)
  2927                  .success(function (data) {
  2928                  data.sort();
  2929                  $scope.tagvs[index][k] = data;
  2930              })
  2931                  .error(function (error) {
  2932                  $scope.error = 'Unable to fetch metrics: ' + error;
  2933              });
  2934          }
  2935          function getRequest() {
  2936              request = new GraphRequest;
  2937              request.start = $scope.start;
  2938              request.end = $scope.end;
  2939              angular.forEach($scope.query_p, function (p) {
  2940                  if (!p.metric) {
  2941                      return;
  2942                  }
  2943                  var q = new Query($scope.filterSupport, p);
  2944                  var tags = q.tags;
  2945                  q.tags = new TagSet;
  2946                  if (!$scope.filterSupport) {
  2947                      angular.forEach(tags, function (v, k) {
  2948                          if (v && k) {
  2949                              q.tags[k] = v;
  2950                          }
  2951                      });
  2952                  }
  2953                  request.queries.push(q);
  2954              });
  2955              return request;
  2956          }
  2957          $scope.keydown = function ($event) {
  2958              if ($event.shiftKey && $event.keyCode == 13) {
  2959                  $scope.Query();
  2960              }
  2961          };
  2962          $scope.Query = function () {
  2963              var r = getRequest();
  2964              angular.forEach($scope.query_p, function (q, index) {
  2965                  var m = q.metric_tags;
  2966                  if (!m) {
  2967                      return;
  2968                  }
  2969                  if (!r.queries[index]) {
  2970                      return;
  2971                  }
  2972                  angular.forEach(q.tags, function (key, tag) {
  2973                      if (m[tag]) {
  2974                          return;
  2975                      }
  2976                      delete r.queries[index].tags[tag];
  2977                  });
  2978                  if ($scope.filterSupport) {
  2979                      _.each(r.queries[index].filters, function (f) {
  2980                          if (m[f.tagk]) {
  2981                              return;
  2982                          }
  2983                          delete r.queries[index].nGbFilters[f.tagk];
  2984                          delete r.queries[index].gbFilters[f.tagk];
  2985                          r.queries[index].filters = _.without(r.queries[index].filters, _.findWhere(r.queries[index].filters, { tagk: f.tagk }));
  2986                      });
  2987                  }
  2988              });
  2989              r.prune();
  2990              $location.search('b64', btoa(JSON.stringify(r)));
  2991              $location.search('autods', $scope.autods ? undefined : 'false');
  2992              $location.search('refresh', $scope.refresh ? 'true' : undefined);
  2993              $location.search('normalize', $scope.normalize ? 'true' : undefined);
  2994              var min = angular.isNumber($scope.min) ? $scope.min.toString() : null;
  2995              var max = angular.isNumber($scope.max) ? $scope.max.toString() : null;
  2996              $location.search('min', min);
  2997              $location.search('max', max);
  2998              $route.reload();
  2999          };
  3000          request = getRequest();
  3001          if (!request.queries.length) {
  3002              return;
  3003          }
  3004          var autods = $scope.autods ? '&autods=' + $('#chart').width() : '';
  3005          function getMetricMeta(metric) {
  3006              $http.get('/api/metadata/metrics?metric=' + encodeURIComponent(metric))
  3007                  .success(function (data) {
  3008                  $scope.meta[metric] = data;
  3009              })
  3010                  .error(function (error) {
  3011                  console.log("Error getting metadata for metric " + metric);
  3012              });
  3013          }
  3014          function get(noRunning) {
  3015              $timeout.cancel(graphRefresh);
  3016              if (!noRunning) {
  3017                  $scope.running = 'Running';
  3018              }
  3019              var autorate = '';
  3020              $scope.meta = {};
  3021              for (var i = 0; i < request.queries.length; i++) {
  3022                  if (request.queries[i].derivative == 'auto') {
  3023                      autorate += '&autorate=' + i;
  3024                  }
  3025                  getMetricMeta(request.queries[i].metric);
  3026              }
  3027              _.each(request.queries, function (q, qIndex) {
  3028                  request.queries[qIndex].filters = _.map(q.filters, function (filter) {
  3029                      var f = new Filter(filter);
  3030                      if (f.filter && f.type) {
  3031                          if (f.type == "auto") {
  3032                              if (f.filter.indexOf("*") > -1) {
  3033                                  f.type = f.filter == "*" ? f.type = "wildcard" : "iwildcard";
  3034                              }
  3035                              else {
  3036                                  f.type = "literal_or";
  3037                              }
  3038                          }
  3039                      }
  3040                      return f;
  3041                  });
  3042              });
  3043              var min = angular.isNumber($scope.min) ? '&min=' + encodeURIComponent($scope.min.toString()) : '';
  3044              var max = angular.isNumber($scope.max) ? '&max=' + encodeURIComponent($scope.max.toString()) : '';
  3045              $scope.animate();
  3046              $scope.queryTime = '';
  3047              if (request.end && !isRel.exec(request.end)) {
  3048                  var t = moment.utc(request.end, moment.defaultFormat);
  3049                  $scope.queryTime = '&date=' + t.format('YYYY-MM-DD');
  3050                  $scope.queryTime += '&time=' + t.format('HH:mm');
  3051              }
  3052              $http.get('/api/graph?' + 'b64=' + encodeURIComponent(btoa(JSON.stringify(request))) + autods + autorate + min + max)
  3053                  .success(function (data) {
  3054                  $scope.result = data.Series;
  3055                  if ($scope.annotateEnabled) {
  3056                      $scope.annotations = _.sortBy(data.Annotations, function (d) { return d.StartDate; });
  3057                  }
  3058                  $scope.warning = '';
  3059                  if (!$scope.result) {
  3060                      $scope.warning = 'No Results';
  3061                  }
  3062                  if (data.Warnings.length > 0) {
  3063                      $scope.warning += data.Warnings.join(" ");
  3064                  }
  3065                  $scope.queries = data.Queries;
  3066                  $scope.exprText = "";
  3067                  _.each($scope.queries, function (q, i) {
  3068                      $scope.exprText += "$" + alphabet[i] + " = " + q + "\n";
  3069                      if (i == $scope.queries.length - 1) {
  3070                          $scope.exprText += "avg($" + alphabet[i] + ")";
  3071                      }
  3072                  });
  3073                  $scope.running = '';
  3074                  $scope.error = '';
  3075                  var u = $location.absUrl();
  3076                  u = u.substr(0, u.indexOf('?')) + '?';
  3077                  u += 'b64=' + search.b64 + autods + autorate + min + max;
  3078                  $scope.url = u;
  3079              })
  3080                  .error(function (error) {
  3081                  $scope.error = error;
  3082                  $scope.running = '';
  3083              })
  3084                  .finally(function () {
  3085                  $scope.stop();
  3086                  if ($scope.refresh) {
  3087                      graphRefresh = $timeout(function () { get(true); }, 5000);
  3088                  }
  3089                  ;
  3090              });
  3091          }
  3092          ;
  3093          get(false);
  3094      }]);
  3095  bosunApp.directive('tsPopup', function () {
  3096      return {
  3097          restrict: 'E',
  3098          scope: {
  3099              url: '=',
  3100          },
  3101          template: '<button class="btn btn-default" data-html="true" data-placement="bottom">embed</button>',
  3102          link: function (scope, elem, attrs) {
  3103              var button = $('button', elem);
  3104              scope.$watch(attrs.url, function (url) {
  3105                  if (!url) {
  3106                      return;
  3107                  }
  3108                  var text = '<input type="text" onClick="this.select();" readonly="readonly" value="&lt;a href=&quot;' + url + '&quot;&gt;&lt;img src=&quot;' + url + '&.png=png&quot;&gt;&lt;/a&gt;">';
  3109                  button.popover({
  3110                      content: text,
  3111                  });
  3112              });
  3113          },
  3114      };
  3115  });
  3116  bosunApp.directive('tsAlertHistory', function () {
  3117      return {
  3118          templateUrl: '/partials/alerthistory.html',
  3119      };
  3120  });
  3121  bosunControllers.controller('HistoryCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
  3122          var search = $location.search();
  3123          var keys = {};
  3124          if (angular.isArray(search.key)) {
  3125              angular.forEach(search.key, function (v) {
  3126                  keys[v] = true;
  3127              });
  3128          }
  3129          else {
  3130              keys[search.key] = true;
  3131          }
  3132          var params = Object.keys(keys).map(function (v) { return 'ak=' + encodeURIComponent(v); }).join('&');
  3133          $http.get('/api/status?' + params + "&all=1")
  3134              .success(function (data) {
  3135              console.log(data);
  3136              var selected_alerts = {};
  3137              angular.forEach(data, function (v, ak) {
  3138                  if (!keys[ak]) {
  3139                      return;
  3140                  }
  3141                  v.Events.map(function (h) { h.Time = moment.utc(h.Time); });
  3142                  angular.forEach(v.Events, function (h, i) {
  3143                      if (i + 1 < v.Events.length) {
  3144                          h.EndTime = v.Events[i + 1].Time;
  3145                      }
  3146                      else {
  3147                          h.EndTime = moment.utc();
  3148                      }
  3149                  });
  3150                  selected_alerts[ak] = {
  3151                      History: v.Events.reverse(),
  3152                  };
  3153              });
  3154              if (Object.keys(selected_alerts).length > 0) {
  3155                  $scope.alert_history = selected_alerts;
  3156              }
  3157              else {
  3158                  $scope.error = 'No Matching Alerts Found';
  3159              }
  3160          })
  3161              .error(function (err) {
  3162              $scope.error = err;
  3163          });
  3164      }]);
  3165  bosunControllers.controller('HostCtrl', ['$scope', '$http', '$location', '$route', function ($scope, $http, $location, $route) {
  3166          var search = $location.search();
  3167          $scope.host = search.host;
  3168          $scope.time = search.time;
  3169          $scope.tab = search.tab || "stats";
  3170          $scope.fsdata = [];
  3171          $scope.metrics = [];
  3172          var currentURL = $location.url();
  3173          $scope.mlink = function (m) {
  3174              var r = new GraphRequest();
  3175              var q = new Query(false);
  3176              q.metric = m;
  3177              q.tags = { 'host': $scope.host };
  3178              r.queries.push(q);
  3179              return r;
  3180          };
  3181          $scope.setTab = function (t) {
  3182              $location.search('tab', t);
  3183              $scope.tab = t;
  3184          };
  3185          $http.get('/api/metric/host/' + $scope.host)
  3186              .success(function (data) {
  3187              $scope.metrics = data || [];
  3188          });
  3189          var start = moment().utc().subtract(parseDuration($scope.time));
  3190          function parseDuration(v) {
  3191              var pattern = /(\d+)(d|y|n|h|m|s)-ago/;
  3192              var m = pattern.exec(v);
  3193              return moment.duration(parseInt(m[1]), m[2].replace('n', 'M'));
  3194          }
  3195          $http.get('/api/metadata/get?tagk=host&tagv=' + encodeURIComponent($scope.host))
  3196              .success(function (data) {
  3197              $scope.metadata = _.filter(data, function (i) {
  3198                  return moment.utc(i.Time) > start;
  3199              });
  3200          });
  3201          var autods = '&autods=100';
  3202          var cpu_r = new GraphRequest();
  3203          cpu_r.start = $scope.time;
  3204          cpu_r.queries = [
  3205              new Query(false, {
  3206                  metric: 'os.cpu',
  3207                  derivative: 'counter',
  3208                  tags: { host: $scope.host },
  3209              })
  3210          ];
  3211          $http.get('/api/graph?' + 'json=' + encodeURIComponent(JSON.stringify(cpu_r)) + autods)
  3212              .success(function (data) {
  3213              if (!data.Series) {
  3214                  return;
  3215              }
  3216              data.Series[0].Name = 'Percent Used';
  3217              $scope.cpu = data.Series;
  3218          });
  3219          var mem_r = new GraphRequest();
  3220          mem_r.start = $scope.time;
  3221          mem_r.queries.push(new Query(false, {
  3222              metric: "os.mem.total",
  3223              tags: { host: $scope.host },
  3224          }));
  3225          mem_r.queries.push(new Query(false, {
  3226              metric: "os.mem.used",
  3227              tags: { host: $scope.host },
  3228          }));
  3229          $http.get('/api/graph?' + 'json=' + encodeURIComponent(JSON.stringify(mem_r)) + autods)
  3230              .success(function (data) {
  3231              if (!data.Series) {
  3232                  return;
  3233              }
  3234              data.Series[1].Name = "Used";
  3235              $scope.mem_total = Math.max.apply(null, data.Series[0].Data.map(function (d) { return d[1]; }));
  3236              $scope.mem = [data.Series[1]];
  3237          });
  3238          var net_bytes_r = new GraphRequest();
  3239          net_bytes_r.start = $scope.time;
  3240          net_bytes_r.queries = [
  3241              new Query(false, {
  3242                  metric: "os.net.bytes",
  3243                  rate: true,
  3244                  rateOptions: { counter: true, resetValue: 1 },
  3245                  tags: { host: $scope.host, iface: "*", direction: "*" },
  3246              })
  3247          ];
  3248          $http.get('/api/graph?' + 'json=' + encodeURIComponent(JSON.stringify(net_bytes_r)) + autods)
  3249              .success(function (data) {
  3250              if (!data.Series) {
  3251                  return;
  3252              }
  3253              var tmp = [];
  3254              var ifaceSeries = {};
  3255              angular.forEach(data.Series, function (series, idx) {
  3256                  series.Data = series.Data.map(function (dp) { return [dp[0], dp[1] * 8]; });
  3257                  if (series.Tags.direction == "out") {
  3258                      series.Data = series.Data.map(function (dp) { return [dp[0], dp[1] * -1]; });
  3259                  }
  3260                  if (!ifaceSeries.hasOwnProperty(series.Tags.iface)) {
  3261                      ifaceSeries[series.Tags.iface] = [series];
  3262                  }
  3263                  else {
  3264                      ifaceSeries[series.Tags.iface].push(series);
  3265                      tmp.push(ifaceSeries[series.Tags.iface]);
  3266                  }
  3267              });
  3268              $scope.idata = tmp;
  3269          });
  3270          var fs_r = new GraphRequest();
  3271          fs_r.start = $scope.time;
  3272          fs_r.queries = [
  3273              new Query(false, {
  3274                  metric: "os.disk.fs.space_total",
  3275                  tags: { host: $scope.host, disk: "*" },
  3276              }),
  3277              new Query(false, {
  3278                  metric: "os.disk.fs.space_used",
  3279                  tags: { host: $scope.host, disk: "*" },
  3280              })
  3281          ];
  3282          $http.get('/api/graph?' + 'json=' + encodeURIComponent(JSON.stringify(fs_r)) + autods)
  3283              .success(function (data) {
  3284              if (!data.Series) {
  3285                  return;
  3286              }
  3287              var tmp = [];
  3288              var fsSeries = {};
  3289              angular.forEach(data.Series, function (series, idx) {
  3290                  var stat = series.Data[series.Data.length - 1][1];
  3291                  var prop = "";
  3292                  if (series.Metric == "os.disk.fs.space_total") {
  3293                      prop = "total";
  3294                  }
  3295                  else {
  3296                      prop = "used";
  3297                  }
  3298                  if (!fsSeries.hasOwnProperty(series.Tags.disk)) {
  3299                      fsSeries[series.Tags.disk] = [series];
  3300                      fsSeries[series.Tags.disk][prop] = stat;
  3301                  }
  3302                  else {
  3303                      fsSeries[series.Tags.disk].push(series);
  3304                      fsSeries[series.Tags.disk][prop] = stat;
  3305                      tmp.push(fsSeries[series.Tags.disk]);
  3306                  }
  3307              });
  3308              $scope.fsdata = tmp;
  3309          });
  3310      }]);
  3311  bosunControllers.controller('IncidentCtrl', ['$scope', '$http', '$location', '$route', '$sce', 'linkService', function ($scope, $http, $location, $route, $sce, linkService) {
  3312          var search = $location.search();
  3313          var id = search.id;
  3314          if (!id) {
  3315              $scope.error = "must supply incident id as query parameter";
  3316              return;
  3317          }
  3318          $http.get('/api/config')
  3319              .success(function (data) {
  3320              $scope.config_text = data;
  3321          });
  3322          $scope.action = function (type) {
  3323              var key = encodeURIComponent($scope.state.AlertKey);
  3324              return '/action?type=' + type + '&key=' + key;
  3325          };
  3326          $scope.getEditSilenceLink = function () {
  3327              return linkService.GetEditSilenceLink($scope.silence, $scope.silenceId);
  3328          };
  3329          $scope.loadTimelinePanel = function (v, i) {
  3330              if (v.doneLoading && !v.error) {
  3331                  return;
  3332              }
  3333              v.error = null;
  3334              v.doneLoading = false;
  3335              if (i == $scope.lastNonUnknownAbnormalIdx && $scope.body) {
  3336                  v.subject = $scope.incident.Subject;
  3337                  v.body = $scope.body;
  3338                  v.doneLoading = true;
  3339                  return;
  3340              }
  3341              var ak = $scope.incident.AlertKey;
  3342              var url = ruleUrl(ak, moment(v.Time));
  3343              $http.post(url, $scope.config_text)
  3344                  .success(function (data) {
  3345                  v.subject = data.Subject;
  3346                  v.body = $sce.trustAsHtml(data.Body);
  3347              })
  3348                  .error(function (error) {
  3349                  v.error = error;
  3350              })
  3351                  .finally(function () {
  3352                  v.doneLoading = true;
  3353              });
  3354          };
  3355          $scope.shown = {};
  3356          $scope.collapse = function (i, v) {
  3357              $scope.shown[i] = !$scope.shown[i];
  3358              if ($scope.loadTimelinePanel && $scope.shown[i]) {
  3359                  $scope.loadTimelinePanel(v, i);
  3360              }
  3361          };
  3362          $scope.time = function (v) {
  3363              var m = moment(v).utc();
  3364              return m.format();
  3365          };
  3366          $http.get('/api/incidents/events?id=' + id)
  3367              .success(function (data) {
  3368              $scope.incident = data;
  3369              $scope.state = $scope.incident;
  3370              $scope.actions = data.Actions;
  3371              $scope.body = $sce.trustAsHtml(data.Body);
  3372              $scope.events = data.Events.reverse();
  3373              $scope.configLink = configUrl($scope.incident.AlertKey, moment.unix($scope.incident.LastAbnormalTime));
  3374              $scope.isActive = data.IsActive;
  3375              $scope.silence = data.Silence;
  3376              $scope.silenceId = data.SilenceId;
  3377              $scope.editSilenceLink = linkService.GetEditSilenceLink($scope.silence, $scope.silenceId);
  3378              for (var i = 0; i < $scope.events.length; i++) {
  3379                  var e = $scope.events[i];
  3380                  if (e.Status != 'normal' && e.Status != 'unknown' && $scope.body) {
  3381                      $scope.lastNonUnknownAbnormalIdx = i;
  3382                      $scope.collapse(i, e); // Expand the panel of the current body
  3383                      break;
  3384                  }
  3385              }
  3386              $scope.collapse;
  3387          })
  3388              .error(function (err) {
  3389              $scope.error = err;
  3390          });
  3391      }]);
  3392  /// <reference path="0-bosun.ts" />
  3393  bosunControllers.controller('ItemsCtrl', ['$scope', '$http', function ($scope, $http) {
  3394          $http.get('/api/metric')
  3395              .success(function (data) {
  3396              $scope.metrics = data;
  3397          })
  3398              .error(function (error) {
  3399              $scope.status = 'Unable to fetch metrics: ' + error;
  3400          });
  3401          $http.get('/api/tagv/host?since=default')
  3402              .success(function (data) {
  3403              $scope.hosts = data;
  3404          })
  3405              .error(function (error) {
  3406              $scope.status = 'Unable to fetch hosts: ' + error;
  3407          });
  3408      }]);
  3409  /// <reference path="0-bosun.ts" />
  3410  var LinkService = /** @class */ (function () {
  3411      function LinkService() {
  3412      }
  3413      LinkService.prototype.GetEditSilenceLink = function (silence, silenceId) {
  3414          if (!(silence && silenceId)) {
  3415              return "";
  3416          }
  3417          var forget = silence.Forget ? '&forget' : '';
  3418          return "/silence?start=" + this.time(silence.Start) +
  3419              "&end=" + this.time(silence.End) +
  3420              "&alert=" + silence.Alert +
  3421              "&tags=" + encodeURIComponent(silence.TagString) +
  3422              forget +
  3423              "&edit=" + silenceId;
  3424      };
  3425      LinkService.prototype.time = function (v) {
  3426          var m = moment(v).utc();
  3427          return m.format();
  3428      };
  3429      return LinkService;
  3430  }());
  3431  bosunApp.service("linkService", LinkService);
  3432  var Tag = /** @class */ (function () {
  3433      function Tag() {
  3434      }
  3435      return Tag;
  3436  }());
  3437  var DP = /** @class */ (function () {
  3438      function DP() {
  3439      }
  3440      return DP;
  3441  }());
  3442  bosunControllers.controller('PutCtrl', ['$scope', '$http', '$route', function ($scope, $http, $route) {
  3443          $scope.tags = [new Tag];
  3444          var dp = new DP;
  3445          dp.k = moment().utc().format();
  3446          $scope.dps = [dp];
  3447          $http.get('/api/metric')
  3448              .success(function (data) {
  3449              $scope.metrics = data;
  3450          })
  3451              .error(function (error) {
  3452              $scope.error = 'Unable to fetch metrics: ' + error;
  3453          });
  3454          $scope.Submit = function () {
  3455              var data = [];
  3456              var tags = {};
  3457              angular.forEach($scope.tags, function (v, k) {
  3458                  if (v.k || v.v) {
  3459                      tags[v.k] = v.v;
  3460                  }
  3461              });
  3462              angular.forEach($scope.dps, function (v, k) {
  3463                  if (v.k && v.v) {
  3464                      var ts = parseInt(moment.utc(v.k, tsdbDateFormat).format('X'));
  3465                      data.push({
  3466                          metric: $scope.metric,
  3467                          timestamp: ts,
  3468                          value: parseFloat(v.v),
  3469                          tags: tags,
  3470                      });
  3471                  }
  3472              });
  3473              $scope.running = 'submitting data...';
  3474              $scope.success = '';
  3475              $scope.error = '';
  3476              $http.post('/api/put', data)
  3477                  .success(function () {
  3478                  $scope.running = '';
  3479                  $scope.success = 'Data Submitted';
  3480              })
  3481                  .error(function (error) {
  3482                  $scope.running = '';
  3483                  $scope.error = error.error.message;
  3484              });
  3485          };
  3486          $scope.AddTag = function () {
  3487              var last = $scope.tags[$scope.tags.length - 1];
  3488              if (last.k && last.v) {
  3489                  $scope.tags.push(new Tag);
  3490              }
  3491          };
  3492          $scope.AddDP = function () {
  3493              var last = $scope.dps[$scope.dps.length - 1];
  3494              if (last.k && last.v) {
  3495                  var dp = new DP;
  3496                  dp.k = moment.utc(last.k, tsdbDateFormat).add(15, 'seconds').format();
  3497                  $scope.dps.push(dp);
  3498              }
  3499          };
  3500          $scope.GetTagKByMetric = function () {
  3501              $http.get('/api/tagk/' + $scope.metric)
  3502                  .success(function (data) {
  3503                  if (!angular.isArray(data)) {
  3504                      return;
  3505                  }
  3506                  $scope.tags = [new Tag];
  3507                  for (var i = 0; i < data.length; i++) {
  3508                      var t = new Tag;
  3509                      t.k = data[i];
  3510                      $scope.tags.push(t);
  3511                  }
  3512              })
  3513                  .error(function (error) {
  3514                  $scope.error = 'Unable to fetch metrics: ' + error;
  3515              });
  3516          };
  3517      }]);
  3518  /// <reference path="0-bosun.ts" />
  3519  bosunControllers.controller('SilenceCtrl', ['$scope', '$http', '$location', '$route', 'linkService', function ($scope, $http, $location, $route, linkService) {
  3520          var search = $location.search();
  3521          $scope.start = search.start;
  3522          $scope.end = search.end;
  3523          $scope.duration = search.duration;
  3524          $scope.alert = search.alert;
  3525          $scope.hosts = search.hosts;
  3526          $scope.tags = search.tags;
  3527          $scope.edit = search.edit;
  3528          $scope.forget = search.forget;
  3529          $scope.message = search.message;
  3530          if (!$scope.end && !$scope.duration) {
  3531              $scope.duration = '1h';
  3532          }
  3533          function filter(data, startBefore, startAfter, endAfter, endBefore, limit) {
  3534              var ret = {};
  3535              var count = 0;
  3536              _.each(data, function (v, name) {
  3537                  if (limit && count >= limit) {
  3538                      return;
  3539                  }
  3540                  var s = moment(v.Start).utc();
  3541                  var e = moment(v.End).utc();
  3542                  if (startBefore && s > startBefore) {
  3543                      return;
  3544                  }
  3545                  if (startAfter && s < startAfter) {
  3546                      return;
  3547                  }
  3548                  if (endAfter && e < endAfter) {
  3549                      return;
  3550                  }
  3551                  if (endBefore && e > endBefore) {
  3552                      return;
  3553                  }
  3554                  ret[name] = v;
  3555              });
  3556              return ret;
  3557          }
  3558          function get() {
  3559              $http.get('/api/silence/get')
  3560                  .success(function (data) {
  3561                  $scope.silences = [];
  3562                  var now = moment.utc();
  3563                  $scope.silences.push({
  3564                      name: 'Active',
  3565                      silences: filter(data, now, null, now, null, 0)
  3566                  });
  3567                  $scope.silences.push({
  3568                      name: 'Upcoming',
  3569                      silences: filter(data, null, now, null, null, 0)
  3570                  });
  3571                  $scope.silences.push({
  3572                      name: 'Past',
  3573                      silences: filter(data, null, null, null, now, 25)
  3574                  });
  3575              })
  3576                  .error(function (error) {
  3577                  $scope.error = error;
  3578              });
  3579          }
  3580          get();
  3581          function getData() {
  3582              var tags = ($scope.tags || '').split(',');
  3583              if ($scope.hosts) {
  3584                  tags.push('host=' + $scope.hosts.split(/[ ,|]+/).join('|'));
  3585              }
  3586              tags = tags.filter(function (v) { return v != ""; });
  3587              var data = {
  3588                  start: $scope.start,
  3589                  end: $scope.end,
  3590                  duration: $scope.duration,
  3591                  alert: $scope.alert,
  3592                  tags: tags.join(','),
  3593                  edit: $scope.edit,
  3594                  forget: $scope.forget ? 'true' : null,
  3595                  message: $scope.message,
  3596              };
  3597              return data;
  3598          }
  3599          var any = search.start || search.end || search.duration || search.alert || search.hosts || search.tags || search.forget;
  3600          var state = getData();
  3601          $scope.change = function () {
  3602              $scope.disableConfirm = true;
  3603          };
  3604          if (any) {
  3605              $scope.error = null;
  3606              $http.post('/api/silence/set', state)
  3607                  .success(function (data) {
  3608                  if (!data) {
  3609                      data = { '(none)': false };
  3610                  }
  3611                  $scope.testSilences = data;
  3612              })
  3613                  .error(function (error) {
  3614                  $scope.error = error;
  3615              });
  3616          }
  3617          $scope.test = function () {
  3618              $location.search('start', $scope.start || null);
  3619              $location.search('end', $scope.end || null);
  3620              $location.search('duration', $scope.duration || null);
  3621              $location.search('alert', $scope.alert || null);
  3622              $location.search('hosts', $scope.hosts || null);
  3623              $location.search('tags', $scope.tags || null);
  3624              $location.search('forget', $scope.forget || null);
  3625              $location.search('message', $scope.message || null);
  3626              $route.reload();
  3627          };
  3628          $scope.confirm = function () {
  3629              $scope.error = null;
  3630              $scope.testSilences = null;
  3631              $scope.edit = null;
  3632              $location.search('edit', null);
  3633              state.confirm = 'true';
  3634              $http.post('/api/silence/set', state)
  3635                  .error(function (error) {
  3636                  $scope.error = error;
  3637              })
  3638                  .finally(get);
  3639          };
  3640          $scope.clear = function (id) {
  3641              if (!window.confirm('Clear this silence?')) {
  3642                  return;
  3643              }
  3644              $scope.error = null;
  3645              $http.post('/api/silence/clear?id=' + id, {})
  3646                  .error(function (error) {
  3647                  $scope.error = error;
  3648              })
  3649                  .finally(get);
  3650          };
  3651          $scope.time = function (v) {
  3652              var m = moment(v).utc();
  3653              return m.format();
  3654          };
  3655          $scope.getEditSilenceLink = function (silence, silenceId) {
  3656              return linkService.GetEditSilenceLink(silence, silenceId);
  3657          };
  3658      }]);
  3659  bosunApp.directive('tsAckGroup', ['$location', '$timeout', function ($location, $timeout) {
  3660          return {
  3661              scope: {
  3662                  ack: '=',
  3663                  groups: '=tsAckGroup',
  3664                  schedule: '=',
  3665                  timeanddate: '=',
  3666              },
  3667              templateUrl: '/partials/ackgroup.html',
  3668              link: function (scope, elem, attrs) {
  3669                  scope.canAckSelected = scope.ack == 'Needs Acknowledgement';
  3670                  scope.panelClass = scope.$parent.panelClass;
  3671                  scope.btoa = scope.$parent.btoa;
  3672                  scope.encode = scope.$parent.encode;
  3673                  scope.shown = {};
  3674                  scope.collapse = function (i) {
  3675                      scope.shown[i] = !scope.shown[i];
  3676                      if (scope.shown[i] && scope.groups[i].Children.length == 1) {
  3677                          $timeout(function () {
  3678                              scope.$broadcast("onOpen", i);
  3679                          }, 0);
  3680                      }
  3681                  };
  3682                  scope.click = function ($event, idx) {
  3683                      scope.collapse(idx);
  3684                      if ($event.shiftKey && scope.schedule.checkIdx != undefined) {
  3685                          var checked = scope.groups[scope.schedule.checkIdx].checked;
  3686                          var start = Math.min(idx, scope.schedule.checkIdx);
  3687                          var end = Math.max(idx, scope.schedule.checkIdx);
  3688                          for (var i = start; i <= end; i++) {
  3689                              if (i == idx) {
  3690                                  continue;
  3691                              }
  3692                              scope.groups[i].checked = checked;
  3693                          }
  3694                      }
  3695                      scope.schedule.checkIdx = idx;
  3696                      scope.update();
  3697                  };
  3698                  scope.select = function (checked) {
  3699                      for (var i = 0; i < scope.groups.length; i++) {
  3700                          scope.groups[i].checked = checked;
  3701                      }
  3702                      scope.update();
  3703                  };
  3704                  scope.update = function () {
  3705                      scope.canCloseSelected = true;
  3706                      scope.canForgetSelected = true;
  3707                      scope.anySelected = false;
  3708                      for (var i = 0; i < scope.groups.length; i++) {
  3709                          var g = scope.groups[i];
  3710                          if (!g.checked) {
  3711                              continue;
  3712                          }
  3713                          scope.anySelected = true;
  3714                          if (g.Status == 'error') {
  3715                              scope.canCloseSelected = false;
  3716                          }
  3717                          if (g.Status != 'unknown') {
  3718                              scope.canForgetSelected = false;
  3719                          }
  3720                      }
  3721                  };
  3722                  scope.multiaction = function (type) {
  3723                      var keys = [];
  3724                      var active = false;
  3725                      angular.forEach(scope.groups, function (group) {
  3726                          if (!group.checked) {
  3727                              return;
  3728                          }
  3729                          if (group.AlertKey) {
  3730                              if (group.State.CurrentStatus != 'normal') {
  3731                                  active = true;
  3732                              }
  3733                              keys.push(group.AlertKey);
  3734                          }
  3735                          angular.forEach(group.Children, function (child) {
  3736                              if (child.State.CurrentStatus != 'normal') {
  3737                                  active = true;
  3738                              }
  3739                              keys.push(child.AlertKey);
  3740                          });
  3741                      });
  3742                      scope.$parent.setKey("action-keys", keys);
  3743                      $location.path("action");
  3744                      $location.search("type", type);
  3745                      $location.search("active", active ? 'true' : 'false');
  3746                  };
  3747                  scope.history = function () {
  3748                      var url = '/history?';
  3749                      angular.forEach(scope.groups, function (group) {
  3750                          if (!group.checked) {
  3751                              return;
  3752                          }
  3753                          if (group.AlertKey) {
  3754                              url += '&key=' + encodeURIComponent(group.AlertKey);
  3755                          }
  3756                          angular.forEach(group.Children, function (child) {
  3757                              url += '&key=' + encodeURIComponent(child.AlertKey);
  3758                          });
  3759                      });
  3760                      return url;
  3761                  };
  3762              },
  3763          };
  3764      }]);
  3765  bosunApp.directive('tsState', ['$sce', '$http', function ($sce, $http) {
  3766          return {
  3767              templateUrl: '/partials/alertstate.html',
  3768              link: function (scope, elem, attrs) {
  3769                  var myIdx = attrs["tsGrp"];
  3770                  scope.currentStatus = attrs["tsGrpstatus"];
  3771                  scope.name = scope.child.AlertKey;
  3772                  scope.state = scope.child.State;
  3773                  scope.action = function (type) {
  3774                      var key = encodeURIComponent(scope.name);
  3775                      var active = scope.state.CurrentStatus != 'normal';
  3776                      return '/action?type=' + type + '&key=' + key + '&active=' + active;
  3777                  };
  3778                  var loadedBody = false;
  3779                  scope.toggle = function () {
  3780                      scope.show = !scope.show;
  3781                      if (scope.show && !loadedBody) {
  3782                          scope.state.Body = "loading...";
  3783                          loadedBody = true;
  3784                          $http.get('/api/status?ak=' + scope.child.AlertKey)
  3785                              .success(function (data) {
  3786                              var body = data[scope.child.AlertKey].Body;
  3787                              scope.state.Body = $sce.trustAsHtml(body);
  3788                          })
  3789                              .error(function (err) {
  3790                              scope.state.Body = "Error loading template body: " + err;
  3791                          });
  3792                      }
  3793                  };
  3794                  scope.$on('onOpen', function (e, i) {
  3795                      if (i == myIdx) {
  3796                          scope.toggle();
  3797                      }
  3798                  });
  3799                  scope.zws = function (v) {
  3800                      if (!v) {
  3801                          return '';
  3802                      }
  3803                      return v.replace(/([,{}()])/g, '$1\u200b');
  3804                  };
  3805                  scope.state.Touched = moment(scope.state.Touched).utc();
  3806                  angular.forEach(scope.state.Events, function (v, k) {
  3807                      v.Time = moment(v.Time).utc();
  3808                  });
  3809                  scope.state.last = scope.state.Events[scope.state.Events.length - 1];
  3810                  if (scope.state.Actions && scope.state.Actions.length > 0) {
  3811                      scope.state.LastAction = scope.state.Actions[scope.state.Actions.length - 1];
  3812                  }
  3813                  scope.state.RuleUrl = '/config?' +
  3814                      'alert=' + encodeURIComponent(scope.state.Alert) +
  3815                      '&fromDate=' + encodeURIComponent(scope.state.last.Time.format("YYYY-MM-DD")) +
  3816                      '&fromTime=' + encodeURIComponent(scope.state.last.Time.format("HH:mm"));
  3817                  var groups = [];
  3818                  angular.forEach(scope.state.Group, function (v, k) {
  3819                      groups.push(k + "=" + v);
  3820                  });
  3821                  if (groups.length > 0) {
  3822                      scope.state.RuleUrl += '&template_group=' + encodeURIComponent(groups.join(','));
  3823                  }
  3824                  scope.state.Body = $sce.trustAsHtml(scope.state.Body);
  3825              },
  3826          };
  3827      }]);
  3828  bosunApp.directive('tsNote', function () {
  3829      return {
  3830          restrict: 'E',
  3831          templateUrl: '/partials/note.html',
  3832      };
  3833  });
  3834  bosunApp.directive('tsAck', function () {
  3835      return {
  3836          restrict: 'E',
  3837          templateUrl: '/partials/ack.html',
  3838      };
  3839  });
  3840  bosunApp.directive('tsClose', function () {
  3841      return {
  3842          restrict: 'E',
  3843          templateUrl: '/partials/close.html',
  3844      };
  3845  });
  3846  bosunApp.directive('tsCancelClose', function () {
  3847      return {
  3848          restrict: 'E',
  3849          templateUrl: '/partials/cancelClose.html',
  3850      };
  3851  });
  3852  bosunApp.directive('tsForget', function () {
  3853      return {
  3854          restrict: 'E',
  3855          templateUrl: '/partials/forget.html',
  3856      };
  3857  });
  3858  bosunApp.directive('tsPurge', function () {
  3859      return {
  3860          restrict: 'E',
  3861          templateUrl: '/partials/purge.html',
  3862      };
  3863  });
  3864  bosunApp.directive('tsForceClose', function () {
  3865      return {
  3866          restrict: 'E',
  3867          templateUrl: '/partials/forceClose.html',
  3868      };
  3869  });
  3870  /// <reference path="0-bosun.ts" />
  3871  var TokenListController = /** @class */ (function () {
  3872      function TokenListController($http, auth) {
  3873          var _this = this;
  3874          this.$http = $http;
  3875          this.auth = auth;
  3876          this.delete = function () {
  3877              _this.status = "Deleting...";
  3878              _this.$http.delete("/api/tokens?hash=" + encodeURIComponent(_this.deleteTarget))
  3879                  .then(function () {
  3880                  _this.status = "";
  3881                  _this.load();
  3882              }, function (err) {
  3883                  _this.status = 'Unable to delete token: ' + err;
  3884              });
  3885          };
  3886          this.load = function () {
  3887              _this.status = "Loading...";
  3888              _this.$http.get("/api/tokens").then(function (resp) {
  3889                  _(resp.data).forEach(function (tok) {
  3890                      tok.LastUsed = moment.utc(tok.LastUsed);
  3891                      tok.Permissions = _this.auth.PermissionsFor(tok.Role);
  3892                      tok.RoleName = _this.auth.RoleFor(tok.Role) || ("" + tok.Permissions.length + " Permissions");
  3893                  });
  3894                  _this.tokens = resp.data;
  3895                  _this.status = "";
  3896              }, function (err) {
  3897                  _this.status = 'Unable to fetch tokens: ' + err;
  3898              });
  3899          };
  3900          this.permList = function (tok) {
  3901              //HACK: return html string for popover. angular-strap has bad api for this
  3902              var h = "<div class=\"popover\" tabindex=\"-1\">\n        <div class=\"arrow\"></div>\n        <div class=\"popover-content\"><ul>";
  3903              var perms = _this.auth.PermissionsFor(tok.Role);
  3904              for (var i = 0; i < perms.length; i++) {
  3905                  var p = perms[i];
  3906                  var open = "<strong>";
  3907                  var close = "</strong>";
  3908                  h += "<li>" + open + p + close + "</li>";
  3909              }
  3910              h += "</ul></div></div>";
  3911              return h;
  3912          };
  3913          this.load();
  3914      }
  3915      TokenListController.$inject = ['$http', "authService"];
  3916      return TokenListController;
  3917  }());
  3918  bosunApp.component('tokenList', {
  3919      controller: TokenListController,
  3920      controllerAs: "ct",
  3921      templateUrl: '/static/partials/tokenList.html'
  3922  });
  3923  /// <reference path="0-bosun.ts" />
  3924  var NewTokenController = /** @class */ (function () {
  3925      function NewTokenController($http, auth) {
  3926          var _this = this;
  3927          this.$http = $http;
  3928          this.auth = auth;
  3929          this.token = new Token();
  3930          this.hasBits = function (bits) {
  3931              return (bits & _this.token.Role) != 0;
  3932          };
  3933          this.setRole = function (bits, event) {
  3934              _(_this.permissions).each(function (perm) {
  3935                  if (!event.currentTarget.checked) {
  3936                      perm.Active = false;
  3937                  }
  3938                  else {
  3939                      perm.Active = (perm.Bits & bits) != 0;
  3940                  }
  3941              });
  3942          };
  3943          this.getBits = function () {
  3944              return _(_this.permissions).reduce(function (sum, p) { return sum + (p.Active ? p.Bits : 0); }, 0);
  3945          };
  3946          var defs = auth.GetRoles();
  3947          this.permissions = defs.Permissions;
  3948          this.roles = defs.Roles;
  3949      }
  3950      NewTokenController.prototype.create = function () {
  3951          var _this = this;
  3952          this.token.Role = this.getBits();
  3953          this.status = "Creating...";
  3954          this.$http.post("/api/tokens", this.token).then(function (resp) {
  3955              _this.status = "";
  3956              _this.createdToken = resp.data.replace(/"/g, "");
  3957          }, function (err) { _this.status = 'Unable to load roles: ' + err; });
  3958      };
  3959      NewTokenController.prototype.encoded = function () {
  3960          return encodeURIComponent(this.createdToken);
  3961      };
  3962      NewTokenController.$inject = ['$http', 'authService'];
  3963      return NewTokenController;
  3964  }());
  3965  bosunApp.component("newToken", {
  3966      controller: NewTokenController,
  3967      controllerAs: "ct",
  3968      templateUrl: "/partials/tokenNew.html"
  3969  });