bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/web/static/js/directives.ts (about)

     1  /// <reference path="0-bosun.ts" />
     2  
     3  bosunApp.directive('tsResults', function() {
     4      return {
     5          templateUrl: '/partials/results.html',
     6          link: (scope: any, elem, attrs) => {
     7              scope.isSeries = v => {
     8                  return typeof (v) === 'object';
     9              };
    10          },
    11      };
    12  });
    13  
    14  bosunApp.directive('tsComputations', () => {
    15      return {
    16          scope: {
    17              computations: '=tsComputations',
    18              time: '=',
    19              header: '=',
    20          },
    21          templateUrl: '/partials/computations.html',
    22          link: (scope: any, elem: any, attrs: any) => {
    23              if (scope.time) {
    24                  var m = moment.utc(scope.time);
    25                  scope.timeParam = "&date=" + encodeURIComponent(m.format("YYYY-MM-DD")) + "&time=" + encodeURIComponent(m.format("HH:mm"));
    26              }
    27              scope.btoa = (v: any) => {
    28                  return encodeURIComponent(btoa(v));
    29              };
    30          },
    31      };
    32  });
    33  
    34  
    35  function fmtDuration(v: any) {
    36      var diff = (moment.duration(v, 'milliseconds'));
    37      var f;
    38      if (Math.abs(v) < 60000) {
    39          return diff.format('ss[s]');
    40      }
    41      return diff.format('d[d]hh[h]mm[m]ss[s]');
    42  }
    43  
    44  
    45  function fmtTime(v: any) {
    46      var m = moment(v).utc();
    47      var now = moment().utc();
    48      var msdiff = now.diff(m);
    49      var ago = '';
    50      var inn = '';
    51      if (msdiff >= 0) {
    52          ago = ' ago';
    53      } else {
    54          inn = 'in ';
    55      }
    56      return m.format() + ' UTC (' + inn + fmtDuration(Math.abs(msdiff)) + ago + ')';
    57  }
    58  
    59  function parseDuration(v: string) {
    60      var pattern = /(\d+)(d|y|n|h|m|s)(-ago)?/;
    61      var m = pattern.exec(v);
    62      if (m) {
    63          return moment.duration(parseInt(m[1]), m[2].replace('n', 'M'))
    64      }
    65      return moment.duration(0)
    66  }
    67  
    68  interface ITimeScope extends IBosunScope {
    69      noLink: string;
    70  }
    71  
    72  bosunApp.directive("tsTime", function() {
    73      return {
    74          link: function(scope: ITimeScope, elem: any, attrs: any) {
    75              scope.$watch(attrs.tsTime, (v: any) => {
    76                  var m = moment(v).utc();
    77                  var text = fmtTime(v);
    78                  if (attrs.tsEndTime) {
    79                      var diff = moment(scope.$eval(attrs.tsEndTime)).diff(m);
    80                      var duration = fmtDuration(diff);
    81                      text += " for " + duration;
    82                  }
    83                  if (attrs.noLink) {
    84                      elem.text(text);
    85                  } else {
    86                      var el = document.createElement('a');
    87                      el.text = text;
    88                      el.href = 'http://www.timeanddate.com/worldclock/converted.html?iso=';
    89                      el.href += m.format('YYYYMMDDTHHmm');
    90                      el.href += '&p1=0';
    91                      angular.forEach(scope.timeanddate, (v, k) => {
    92                          el.href += '&p' + (k + 2) + '=' + v;
    93                      });
    94                      elem.html(el);
    95                  }
    96              });
    97          },
    98      };
    99  });
   100  
   101  bosunApp.directive("tsTimeUnix", function() {
   102      return {
   103          link: function(scope: ITimeScope, elem: any, attrs: any) {
   104              scope.$watch(attrs.tsTimeUnix, (v: any) => {
   105                  var m = moment(v * 1000).utc();
   106                  var text = fmtTime(m);
   107                  if (attrs.tsEndTime) {
   108                      var diff = moment(scope.$eval(attrs.tsEndTime)).diff(m);
   109                      var duration = fmtDuration(diff);
   110                      text += " for " + duration;
   111                  }
   112                  if (attrs.noLink) {
   113                      elem.text(text);
   114                  } else {
   115                      var el = document.createElement('a');
   116                      el.text = text;
   117                      el.href = 'http://www.timeanddate.com/worldclock/converted.html?iso=';
   118                      el.href += m.format('YYYYMMDDTHHmm');
   119                      el.href += '&p1=0';
   120                      angular.forEach(scope.timeanddate, (v, k) => {
   121                          el.href += '&p' + (k + 2) + '=' + v;
   122                      });
   123                      elem.html(el);
   124                  }
   125              });
   126          },
   127      };
   128  });
   129  
   130  bosunApp.directive("tsSince", function() {
   131      return {
   132          link: function(scope: IBosunScope, elem: any, attrs: any) {
   133              scope.$watch(attrs.tsSince, (v: any) => {
   134                  var m = moment(v).utc();
   135                  elem.text(m.fromNow());
   136              });
   137          },
   138      };
   139  });
   140  
   141  bosunApp.directive("tooltip", function() {
   142      return {
   143          link: function(scope: IGraphScope, elem: any, attrs: any) {
   144              angular.element(elem[0]).tooltip({ placement: "bottom" });
   145          },
   146      };
   147  });
   148  
   149  
   150  bosunApp.directive('tsTab', () => {
   151      return {
   152          link: (scope: any, elem: any, attrs: any) => {
   153              var ta = elem[0];
   154              elem.keydown(evt => {
   155                  if (evt.ctrlKey) {
   156                      return;
   157                  }
   158                  // This is so shift-enter can be caught to run a rule when tsTab is called from
   159                  // the rule page
   160                  if (evt.keyCode == 13 && evt.shiftKey) {
   161                      return;
   162                  }
   163                  switch (evt.keyCode) {
   164                      case 9: // tab
   165                          evt.preventDefault();
   166                          var v = ta.value;
   167                          var start = ta.selectionStart;
   168                          ta.value = v.substr(0, start) + "\t" + v.substr(start);
   169                          ta.selectionStart = ta.selectionEnd = start + 1;
   170                          return;
   171                      case 13: // enter
   172                          if (ta.selectionStart != ta.selectionEnd) {
   173                              return;
   174                          }
   175                          evt.preventDefault();
   176                          var v = ta.value;
   177                          var start = ta.selectionStart;
   178                          var sub = v.substr(0, start);
   179                          var last = sub.lastIndexOf("\n") + 1
   180                          for (var i = last; i < sub.length && /[ \t]/.test(sub[i]); i++)
   181                              ;
   182                          var ws = sub.substr(last, i - last);
   183                          ta.value = v.substr(0, start) + "\n" + ws + v.substr(start);
   184                          ta.selectionStart = ta.selectionEnd = start + 1 + ws.length;
   185                  }
   186              });
   187          },
   188      };
   189  });
   190  
   191  interface JQuery {
   192      tablesorter(v: any): JQuery;
   193      linedtextarea(): void;
   194  }
   195  
   196  bosunApp.directive('tsresizable', () => {
   197      return {
   198          restrict: 'A',
   199          scope: {
   200              callback: '&onResize'
   201          },
   202          link: function postLink(scope: any, elem: any, attrs) {
   203              elem.resizable();
   204              elem.on('resizestop', function(evt, ui) {
   205                  if (scope.callback) { scope.callback(); }
   206              });
   207          }
   208      };
   209  });
   210  
   211  bosunApp.directive('tsTableSort', ['$timeout', ($timeout: ng.ITimeoutService) => {
   212      return {
   213          link: (scope: ng.IScope, elem: any, attrs: any) => {
   214              $timeout(() => {
   215                  $(elem).tablesorter({
   216                      sortList: scope.$eval(attrs.tsTableSort),
   217                  });
   218              });
   219          },
   220      };
   221  }]);
   222  
   223  // https://gist.github.com/mlynch/dd407b93ed288d499778
   224  bosunApp.directive('autofocus', ['$timeout', function($timeout) {
   225    return {
   226      restrict: 'A',
   227      link : function($scope, $element) {
   228        $timeout(function() {
   229          $element[0].focus();
   230        });
   231      }
   232    }
   233  }]);
   234  
   235  bosunApp.directive('tsTimeLine', () => {
   236      var tsdbFormat = d3.time.format.utc("%Y/%m/%d-%X");
   237      function parseDate(s: any) {
   238          return moment.utc(s).toDate();
   239      }
   240      var margin = {
   241          top: 10,
   242          right: 10,
   243          bottom: 30,
   244          left: 250,
   245      };
   246      return {
   247          link: (scope: any, elem: any, attrs: any) => {
   248              scope.shown = {};
   249              scope.collapse = (i: any, entry: any, v: any) => {
   250                  scope.shown[i] = !scope.shown[i];
   251                  if (scope.loadTimelinePanel && entry && scope.shown[i]) {
   252                      scope.loadTimelinePanel(entry, v);
   253                  }
   254              };
   255              scope.$watch('alert_history', update);
   256              function update(history: any) {
   257                  if (!history) {
   258                      return;
   259                  }
   260                  var entries = d3.entries(history);
   261                  if (!entries.length) {
   262                      return;
   263                  }
   264                  entries.sort((a, b) => {
   265                      return a.key.localeCompare(b.key);
   266                  });
   267                  scope.entries = entries;
   268                  var values = entries.map(v => { return v.value });
   269                  var keys = entries.map(v => { return v.key });
   270                  var barheight = 500 / values.length;
   271                  barheight = Math.min(barheight, 45);
   272                  barheight = Math.max(barheight, 15);
   273                  var svgHeight = values.length * barheight + margin.top + margin.bottom;
   274                  var height = svgHeight - margin.top - margin.bottom;
   275                  var svgWidth = elem.width();
   276                  var width = svgWidth - margin.left - margin.right;
   277                  var xScale = d3.time.scale.utc().range([0, width]);
   278                  var xAxis = d3.svg.axis()
   279                      .scale(xScale)
   280                      .orient('bottom');
   281                  elem.empty();
   282                  var svg = d3.select(elem[0])
   283                      .append('svg')
   284                      .attr('width', svgWidth)
   285                      .attr('height', svgHeight)
   286                      .append('g')
   287                      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
   288                  svg.append('g')
   289                      .attr('class', 'x axis tl-axis')
   290                      .attr('transform', 'translate(0,' + height + ')');
   291                  xScale.domain([
   292                      d3.min(values, (d: any) => { return d3.min(d.History, (c: any) => { return parseDate(c.Time); }); }),
   293                      d3.max(values, (d: any) => { return d3.max(d.History, (c: any) => { return parseDate(c.EndTime); }); }),
   294                  ]);
   295                  var legend = d3.select(elem[0])
   296                      .append('div')
   297                      .attr('class', 'tl-legend');
   298                  var time_legend = legend
   299                      .append('div')
   300                      .text(values[0].History[0].Time);
   301                  var alert_legend = legend
   302                      .append('div')
   303                      .text(keys[0]);
   304                  svg.select('.x.axis')
   305                      .transition()
   306                      .call(xAxis);
   307                  var chart = svg.append('g');
   308                  angular.forEach(entries, function(entry: any, i: number) {
   309                      chart.selectAll('.bars')
   310                          .data(entry.value.History)
   311                          .enter()
   312                          .append('rect')
   313                          .attr('class', (d: any) => { return 'tl-' + d.Status; })
   314                          .attr('x', (d: any) => { return xScale(parseDate(d.Time)); })
   315                          .attr('y', i * barheight)
   316                          .attr('height', barheight)
   317                          .attr('width', (d: any) => {
   318                              return xScale(parseDate(d.EndTime)) - xScale(parseDate(d.Time));
   319                          })
   320                          .on('mousemove.x', mousemove_x)
   321                          .on('mousemove.y', function(d) {
   322                              alert_legend.text(entry.key);
   323                          })
   324                          .on('click', function(d, j) {
   325                              var id = 'panel' + i + '-' + j;
   326                              scope.shown['group' + i] = true;
   327                              scope.shown[id] = true;
   328                              if (scope.loadTimelinePanel) {
   329                                  scope.loadTimelinePanel(entry, d);
   330                              }
   331  
   332                              scope.$apply();
   333                              setTimeout(() => {
   334                                  var e = $("#" + id);
   335                                  if (!e) {
   336                                      console.log('no', id, e);
   337                                      return;
   338                                  }
   339                                  $('html, body').scrollTop(e.offset().top);
   340                              });
   341                          });
   342                  });
   343                  chart.selectAll('.labels')
   344                      .data(keys)
   345                      .enter()
   346                      .append('text')
   347                      .attr('text-anchor', 'end')
   348                      .attr('x', 0)
   349                      .attr('dx', '-.5em')
   350                      .attr('dy', '.25em')
   351                      .attr('y', function(d: any, i: number) { return (i + .5) * barheight; })
   352                      .text(function(d: any) { return d; });
   353                  chart.selectAll('.sep')
   354                      .data(values)
   355                      .enter()
   356                      .append('rect')
   357                      .attr('y', function(d: any, i: number) { return (i + 1) * barheight })
   358                      .attr('height', 1)
   359                      .attr('x', 0)
   360                      .attr('width', width)
   361                      .on('mousemove.x', mousemove_x);
   362                  function mousemove_x() {
   363                      var x = xScale.invert(d3.mouse(this)[0]);
   364                      time_legend
   365                          .text(tsdbFormat(x));
   366                  }
   367              };
   368          },
   369      };
   370  });
   371  
   372  var fmtUnits = ['', 'k', 'M', 'G', 'T', 'P', 'E'];
   373  
   374  function nfmt(s: any, mult: number, suffix: string, opts: any) {
   375      opts = opts || {};
   376      var n = parseFloat(s);
   377      if (isNaN(n) && typeof s === 'string') {
   378          return s;
   379      }
   380      if (opts.round) n = Math.round(n);
   381      if (!n) return suffix ? '0 ' + suffix : '0';
   382      if (isNaN(n) || !isFinite(n)) return '-';
   383      var a = Math.abs(n);
   384      if (a >= 1) {
   385          var number = Math.floor(Math.log(a) / Math.log(mult));
   386          a /= Math.pow(mult, Math.floor(number));
   387          if (fmtUnits[number]) {
   388              suffix = fmtUnits[number] + suffix;
   389          }
   390      }
   391      var r = a.toFixed(5);
   392      if (a < 1e-5) {
   393          r = a.toString();
   394      }
   395      var neg = n < 0 ? '-' : '';
   396      return neg + (+r) + suffix;
   397  }
   398  
   399  bosunApp.filter('nfmt', function() {
   400      return function(s: any) {
   401          return nfmt(s, 1000, '', {});
   402      }
   403  });
   404  
   405  bosunApp.filter('bytes', function() {
   406      return function(s: any) {
   407          return nfmt(s, 1024, 'B', { round: true });
   408      }
   409  });
   410  
   411  bosunApp.filter('bits', function() {
   412      return function(s: any) {
   413          return nfmt(s, 1024, 'b', { round: true });
   414      }
   415  });
   416  
   417  
   418  bosunApp.directive('elastic', [
   419      '$timeout',
   420      function($timeout) {
   421          return {
   422              restrict: 'A',
   423              link: function($scope, element) {
   424                  $scope.initialHeight = $scope.initialHeight || element[0].style.height;
   425                  var resize = function() {
   426                      element[0].style.height = $scope.initialHeight;
   427                      element[0].style.height = "" + element[0].scrollHeight + "px";
   428                  };
   429                  element.on("input change", resize);
   430                  $timeout(resize, 0);
   431              }
   432          };
   433      }
   434  ]);
   435  
   436  bosunApp.directive('tsBar', ['$window', 'nfmtFilter', function($window: ng.IWindowService, fmtfilter: any) {
   437      var margin = {
   438          top: 20,
   439          right: 20,
   440          bottom: 0,
   441          left: 200,
   442      };
   443      return {
   444          scope: {
   445              data: '=',
   446              height: '=',
   447          },
   448          link: (scope: any, elem: any, attrs: any) => {
   449              var svgHeight = +scope.height || 150;
   450              var height = svgHeight - margin.top - margin.bottom;
   451              var svgWidth: number;
   452              var width: number;
   453              var xScale = d3.scale.linear();
   454              var yScale = d3.scale.ordinal()
   455              var top = d3.select(elem[0])
   456                  .append('svg')
   457                  .attr('height', svgHeight)
   458                  .attr('width', '100%');
   459              var svg = top
   460                  .append('g')
   461              //.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
   462              var xAxis = d3.svg.axis()
   463                  .scale(xScale)
   464                  .orient("top")
   465              var yAxis = d3.svg.axis()
   466                  .scale(yScale)
   467                  .orient("left")
   468              scope.$watch('data', update);
   469              var w = angular.element($window);
   470              scope.$watch(() => {
   471                  return w.width();
   472              }, resize, true);
   473              w.bind('resize', () => {
   474                  scope.$apply();
   475              });
   476              function resize() {
   477                  if (!scope.data) {
   478                      return;
   479                  }
   480                  svgWidth = elem.width();
   481                  if (svgWidth <= 0) {
   482                      return;
   483                  }
   484                  margin.left = d3.max(scope.data, (d: any) => { return d.name.length * 8 })
   485                  width = svgWidth - margin.left - margin.right;
   486                  svgHeight = scope.data.length * 15;
   487                  height = svgHeight - margin.top - margin.bottom;
   488                  xScale.range([0, width]);
   489                  yScale.rangeRoundBands([0, height], .1);
   490                  yAxis.scale(yScale);
   491                  svg.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
   492                  svg.attr('width', svgWidth);
   493                  svg.attr('height', height);
   494                  top.attr('height', svgHeight);
   495                  xAxis.ticks(width / 60);
   496                  draw();
   497              }
   498              function update(v: any) {
   499                  if (!angular.isArray(v) || v.length == 0) {
   500                      return;
   501                  }
   502                  resize();
   503              }
   504              function draw() {
   505                  if (!scope.data) {
   506                      return;
   507                  }
   508                  yScale.domain(scope.data.map((d: any) => { return d.name }));
   509                  xScale.domain([0, d3.max(scope.data, (d: any) => { return d.Value })]);
   510                  svg.selectAll('g.axis').remove();
   511                  //X axis
   512                  svg.append("g")
   513                      .attr("class", "x axis")
   514                      .call(xAxis)
   515                  svg.append("g")
   516                      .attr("class", "y axis")
   517                      .call(yAxis)
   518                      .selectAll("text")
   519                      .style("text-anchor", "end")
   520                  var bars = svg.selectAll(".bar").data(scope.data);
   521                  bars.enter()
   522                      .append("rect")
   523                      .attr("class", "bar")
   524                      .attr("y", function(d) { return yScale(d.name); })
   525                      .attr("height", yScale.rangeBand())
   526                      .attr('width', (d: any) => { return xScale(d.Value); })
   527              };
   528          },
   529      };
   530  }]);
   531  
   532  bosunApp.directive('tsGraph', ['$window', 'nfmtFilter', function($window: ng.IWindowService, fmtfilter: any) {
   533      var margin = {
   534          top: 10,
   535          right: 10,
   536          bottom: 30,
   537          left: 80,
   538      };
   539      return {
   540          scope: {
   541              data: '=',
   542              annotations: '=',
   543              height: '=',
   544              generator: '=',
   545              brushStart: '=bstart',
   546              brushEnd: '=bend',
   547              enableBrush: '@',
   548              max: '=',
   549              min: '=',
   550              normalize: '=',
   551              annotation: '=',
   552              annotateEnabled: '=',
   553              showAnnotations: '=',
   554          },
   555          template: '<div class="row"></div>' + // chartElemt
   556          '<div class="row col-lg-12"></div>' + // timeElem
   557          '<div class"row">' + // legendAnnContainer
   558          '<div class="col-lg-6"></div>' + // legendElem
   559          '<div class="col-lg-6"></div>' + // annElem
   560          '</div>',
   561          link: (scope: any, elem: any, attrs: any, $compile: any) => {
   562              var chartElem = d3.select(elem.children()[0]);
   563              var timeElem = d3.select(elem.children()[1]);
   564              var legendAnnContainer = angular.element(elem.children()[2]);
   565              var legendElem = d3.select(legendAnnContainer.children()[0]);
   566              if (scope.annotateEnabled) {
   567                  var annElem = d3.select(legendAnnContainer.children()[1]);
   568              }
   569              var valueIdx = 1;
   570              if (scope.normalize) {
   571                  valueIdx = 2;
   572              }
   573              var svgHeight = +scope.height || 150;
   574              var height = svgHeight - margin.top - margin.bottom;
   575              var svgWidth: number;
   576              var width: number;
   577              var yScale = d3.scale.linear().range([height, 0]);
   578              var xScale = d3.time.scale.utc();
   579              var xAxis = d3.svg.axis()
   580                  .orient('bottom');
   581              var yAxis = d3.svg.axis()
   582                  .scale(yScale)
   583                  .orient('left')
   584                  .ticks(Math.min(10, height / 20))
   585                  .tickFormat(fmtfilter);
   586              var line: any;
   587              switch (scope.generator) {
   588                  case 'area':
   589                      line = d3.svg.area();
   590                      break;
   591                  default:
   592                      line = d3.svg.line();
   593              }
   594              var brush = d3.svg.brush()
   595                  .x(xScale)
   596                  .on('brush', brushed)
   597                  .on('brushend', annotateBrushed);
   598              var top = chartElem
   599                  .append('svg')
   600                  .attr('height', svgHeight)
   601                  .attr('width', '100%');
   602              var svg = top
   603                  .append('g')
   604                  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
   605              var defs = svg.append('defs')
   606                  .append('clipPath')
   607                  .attr('id', 'clip')
   608                  .append('rect')
   609                  .attr('height', height);
   610              var chart = svg.append('g')
   611                  .attr('pointer-events', 'all')
   612                  .attr('clip-path', 'url(#clip)');
   613              svg.append('g')
   614                  .attr('class', 'x axis')
   615                  .attr('transform', 'translate(0,' + height + ')');
   616              svg.append('g')
   617                  .attr('class', 'y axis');
   618              var paths = chart.append('g');
   619              chart.append('g')
   620                  .attr('class', 'x brush');
   621              if (scope.annotateEnabled) {
   622                  var ann = chart.append('g');
   623              }
   624              top.append('rect')
   625                  .style('opacity', 0)
   626                  .attr('x', 0)
   627                  .attr('y', 0)
   628                  .attr('height', height)
   629                  .attr('width', margin.left)
   630                  .style('cursor', 'pointer')
   631                  .on('click', yaxisToggle);
   632              var xloc = timeElem.append('div').attr("class", "col-lg-6");
   633              xloc.style('float', 'left');
   634              var brushText = timeElem.append('div').attr("class", "col-lg-6").append('p').attr("class", "text-right")
   635              var legend = legendElem;
   636              var aLegend = annElem;
   637  
   638              var color = d3.scale.ordinal().range([
   639                  '#e41a1c',
   640                  '#377eb8',
   641                  '#4daf4a',
   642                  '#984ea3',
   643                  '#ff7f00',
   644                  '#a65628',
   645                  '#f781bf',
   646                  '#999999',
   647              ]);
   648              var annColor = d3.scale.ordinal().range([
   649                  '#e41a1c',
   650                  '#377eb8',
   651                  '#4daf4a',
   652                  '#984ea3',
   653                  '#ff7f00',
   654                  '#a65628',
   655                  '#f781bf',
   656                  '#999999',
   657              ]);
   658              var mousex = 0;
   659              var mousey = 0;
   660              var oldx = 0;
   661              var hover = svg.append('g')
   662                  .attr('class', 'hover')
   663                  .style('pointer-events', 'none')
   664                  .style('display', 'none');
   665              var hoverPoint = hover.append('svg:circle')
   666                  .attr('r', 5);
   667              var hoverRect = hover.append('svg:rect')
   668                  .attr('fill', 'white');
   669              var hoverText = hover.append('svg:text')
   670                  .style('font-size', '12px');
   671              var focus = svg.append('g')
   672                  .attr('class', 'focus')
   673                  .style('pointer-events', 'none');
   674              focus.append('line');
   675              var yaxisZero = false;
   676              function yaxisToggle() {
   677                  yaxisZero = !yaxisZero;
   678                  draw();
   679              }
   680  
   681              var drawAnnLegend = () => {
   682                  if (scope.annotation) {
   683                      aLegend.html('')
   684                      var a = scope.annotation;
   685                      //var table = aLegend.append('table').attr("class", "table table-condensed")
   686                      var table = aLegend.append("div")
   687                      var row = table.append("div").attr("class", "row")
   688                      row.append("div").attr("class", "col-lg-2").text("CreationUser")
   689                      row.append("div").attr("class", "col-lg-10").text(a.CreationUser)
   690                      row = table.append("div").attr("class", "row")
   691                      row.append("div").attr("class", "col-lg-2").text("Owner")
   692                      row.append("div").attr("class", "col-lg-10").text(a.Owner)
   693                      row = table.append("div").attr("class", "row")
   694                      row.append("div").attr("class", "col-lg-2").text("Url")
   695                      row.append("div").attr("class", "col-lg-10").append('a')
   696                          .attr("xlink:href", a.Url).text(a.Url).on("click", (d) => {
   697                              window.open(a.Url, "_blank");
   698                          });
   699                      row = table.append("div").attr("class", "row")
   700                      row.append("div").attr("class", "col-lg-2").text("Category")
   701                      row.append("div").attr("class", "col-lg-10").text(a.Category)
   702                      row = table.append("div").attr("class", "row")
   703                      row.append("div").attr("class", "col-lg-2").text("Host")
   704                      row.append("div").attr("class", "col-lg-10").text(a.Host)
   705                      row = table.append("div").attr("class", "row")
   706                      row.append("div").attr("class", "col-lg-2").text("Message")
   707                      row.append("div").attr("class", "col-lg-10").text(a.Message)
   708                  }//
   709              };
   710  
   711              var drawLegend = _.throttle((normalizeIdx: any) => {
   712                  var names = legend.selectAll('.series')
   713                      .data(scope.data, (d) => { return d.Name; });
   714                  names.enter()
   715                      .append('div')
   716                      .attr('class', 'series');
   717                  names.exit()
   718                      .remove();
   719  
   720  
   721                  var xi = xScale.invert(mousex);
   722                  xloc.text('Time: ' + fmtTime(xi));
   723                  var t = xi.getTime() / 1000;
   724                  var minDist = width + height;
   725                  var minName: string, minColor: string;
   726                  var minX: number, minY: number;
   727  
   728                  names
   729                      .each(function(d: any) {
   730                          var idx = bisect(d.Data, t);
   731                          if (idx >= d.Data.length) {
   732                              idx = d.Data.length - 1;
   733                          }
   734                          var e = d3.select(this);
   735                          var pt = d.Data[idx];
   736                          if (pt) {
   737                              e.attr('title', pt[normalizeIdx]);
   738                              e.text(d.Name + ': ' + fmtfilter(pt[1]));
   739                              var ptx = xScale(pt[0] * 1000);
   740                              var pty = yScale(pt[normalizeIdx]);
   741                              var ptd = Math.sqrt(
   742                                  Math.pow(ptx - mousex, 2) +
   743                                  Math.pow(pty - mousey, 2)
   744                              );
   745                              if (ptd < minDist) {
   746                                  minDist = ptd;
   747                                  minX = ptx;
   748                                  minY = pty;
   749                                  minName = d.Name + ': ' + pt[1];
   750                                  minColor = color(d.Name);
   751                              }
   752                          }
   753                      })
   754                      .style('color', (d: any) => { return color(d.Name); });
   755                  hover
   756                      .attr('transform', 'translate(' + minX + ',' + minY + ')');
   757                  hoverPoint.style('fill', minColor);
   758                  hoverText
   759                      .text(minName)
   760                      .style('fill', minColor);
   761                  var isRight = minX > width / 2;
   762                  var isBottom = minY > height / 2;
   763                  hoverText
   764                      .attr('x', isRight ? -5 : 5)
   765                      .attr('y', isBottom ? -8 : 15)
   766                      .attr('text-anchor', isRight ? 'end' : 'start');
   767                  var node: any = hoverText.node();
   768                  var bb = node.getBBox();
   769                  hoverRect
   770                      .attr('x', bb.x - 1)
   771                      .attr('y', bb.y - 1)
   772                      .attr('height', bb.height + 2)
   773                      .attr('width', bb.width + 2);
   774                  var x = mousex;
   775                  if (x > width) {
   776                      x = 0;
   777                  }
   778                  focus.select('line')
   779                      .attr('x1', x)
   780                      .attr('x2', x)
   781                      .attr('y1', 0)
   782                      .attr('y2', height);
   783                  if (extentStart) {
   784                      var s = extentStart;
   785                      if (extentEnd != extentStart) {
   786                          s += ' - ' + extentEnd;
   787                          s += ' (' + extentDiff + ')'
   788                      }
   789                      brushText.text(s);
   790                  }
   791              }, 50);
   792  
   793              scope.$watchCollection('[data, annotations, showAnnotations]', update);
   794              var showAnnotations = (show: boolean) => {
   795                  if (show) {
   796                      ann.attr("visibility", "visible");
   797                      return;
   798                  }
   799                  ann.attr("visibility", "hidden");
   800                  aLegend.html('');
   801              }
   802              var w = angular.element($window);
   803              scope.$watch(() => {
   804                  return w.width();
   805              }, resize, true);
   806              w.bind('resize', () => {
   807                  scope.$apply();
   808              });
   809              function resize() {
   810                  svgWidth = elem.width();
   811                  if (svgWidth <= 0) {
   812                      return;
   813                  }
   814                  width = svgWidth - margin.left - margin.right;
   815                  xScale.range([0, width]);
   816                  xAxis.scale(xScale);
   817                  if (!mousex) {
   818                      mousex = width + 1;
   819                  }
   820                  svg.attr('width', svgWidth);
   821                  defs.attr('width', width);
   822                  xAxis.ticks(width / 60);
   823                  draw();
   824              }
   825              var oldx = 0;
   826              var bisect = d3.bisector((d) => { return d[0]; }).left;
   827              var bisectA = d3.bisector((d) => { return moment(d.StartDate).unix(); }).left;
   828              function update(v: any) {
   829                  if (!angular.isArray(v) || v.length == 0) {
   830                      return;
   831                  }
   832                  d3.selectAll(".x.brush").call(brush.clear());
   833                  if (scope.annotateEnabled) {
   834                      showAnnotations(scope.showAnnotations);
   835                  }
   836                  resize();
   837              }
   838              function draw() {
   839                  if (!scope.data) {
   840                      return;
   841                  }
   842                  if (scope.normalize) {
   843                      valueIdx = 2;
   844                  }
   845                  function mousemove() {
   846                      var pt = d3.mouse(this);
   847                      mousex = pt[0];
   848                      mousey = pt[1];
   849                      drawLegend(valueIdx);
   850                  }
   851                  scope.data.map((data: any, i: any) => {
   852                      var max = d3.max(data.Data, (d: any) => { return d[1]; });
   853                      data.Data.map((d: any, j: any) => {
   854                          d.push(d[1] / max * 100 || 0)
   855                      });
   856                  });
   857                  line.y((d: any) => { return yScale(d[valueIdx]); });
   858                  line.x((d: any) => { return xScale(d[0] * 1000); });
   859                  var xdomain = [
   860                      d3.min(scope.data, (d: any) => { return d3.min(d.Data, (c: any) => { return c[0]; }); }) * 1000,
   861                      d3.max(scope.data, (d: any) => { return d3.max(d.Data, (c: any) => { return c[0]; }); }) * 1000,
   862                  ];
   863                  if (!oldx) {
   864                      oldx = xdomain[1];
   865                  }
   866                  xScale.domain(xdomain);
   867                  var ymin = d3.min(scope.data, (d: any) => { return d3.min(d.Data, (c: any) => { return c[1]; }); });
   868                  var ymax = d3.max(scope.data, (d: any) => { return d3.max(d.Data, (c: any) => { return c[valueIdx]; }); });
   869                  var diff = (ymax - ymin) / 50;
   870                  if (!diff) {
   871                      diff = 1;
   872                  }
   873                  ymin -= diff;
   874                  ymax += diff;
   875                  if (yaxisZero) {
   876                      if (ymin > 0) {
   877                          ymin = 0;
   878                      } else if (ymax < 0) {
   879                          ymax = 0;
   880                      }
   881                  }
   882                  var ydomain = [ymin, ymax];
   883                  if (angular.isNumber(scope.min)) {
   884                      ydomain[0] = +scope.min;
   885                  }
   886                  if (angular.isNumber(scope.max)) {
   887                      ydomain[valueIdx] = +scope.max;
   888                  }
   889                  yScale.domain(ydomain);
   890                  if (scope.generator == 'area') {
   891                      line.y0(yScale(0));
   892                  }
   893                  svg.select('.x.axis')
   894                      .transition()
   895                      .call(xAxis);
   896                  svg.select('.y.axis')
   897                      .transition()
   898                      .call(yAxis);
   899                  svg.append('text')
   900                      .attr("class", "ylabel")
   901                      .attr("transform", "rotate(-90)")
   902                      .attr("y", -margin.left)
   903                      .attr("x", - (height / 2))
   904                      .attr("dy", "1em")
   905                      .text(_.uniq(scope.data.map(v => { return v.Unit })).join("; "));
   906  
   907                  if (scope.annotateEnabled) {
   908                      var rowId = {}; // annotation Id -> rowId
   909                      var rowEndDate = {}; // rowId -> EndDate
   910                      var maxRow = 0;
   911                      for (var i = 0; i < scope.annotations.length; i++) {
   912                          if (i == 0) {
   913                              rowId[scope.annotations[i].Id] = 0;
   914                              rowEndDate[0] = scope.annotations[0].EndDate;
   915                              continue;
   916                          }
   917                          for (var row = 0; row <= maxRow + 1; row++) {
   918                              if (row == maxRow + 1) {
   919                                  rowId[scope.annotations[i].Id] = row;
   920                                  rowEndDate[row] = scope.annotations[i].EndDate;
   921                                  maxRow += 1
   922                                  break;
   923                              }
   924                              if (rowEndDate[row] < scope.annotations[i].StartDate) {
   925                                  rowId[scope.annotations[i].Id] = row;
   926                                  rowEndDate[row] = scope.annotations[i].EndDate;
   927                                  break;
   928                              }
   929                          }
   930                      }
   931                      var annotations = ann.selectAll('.annotation')
   932                          .data(scope.annotations, (d) => { return d.Id; });
   933                      annotations.enter()
   934                          .append("svg:a")
   935                          .append('rect')
   936                          .attr('visilibity', () => {
   937                              if (scope.showAnnotations) {
   938                                  return "visible";
   939                              }
   940                              return "hidden";
   941                          })
   942                          .attr("y", (d) => { return rowId[d.Id] * ((height * .05) + 2) })
   943                          .attr("height", height * .05)
   944                          .attr("class", "annotation")
   945                          .attr("stroke", (d) => { return annColor(d.Id) })
   946                          .attr("stroke-opacity", .5)
   947                          .attr("fill", (d) => { return annColor(d.Id) })
   948                          .attr("fill-opacity", 0.1)
   949                          .attr("stroke-width", 1)
   950                          .attr("x", (d: any) => { return xScale(moment(d.StartDate).utc().unix() * 1000); })
   951                          .attr("width", (d: any) => {
   952                              var startT = moment(d.StartDate).utc().unix() * 1000
   953                              var endT = moment(d.EndDate).utc().unix() * 1000
   954                              var calcWidth = xScale(endT) - xScale(startT)
   955                              // Never render boxes with less than 8 pixels are they are difficult to click
   956                              if (calcWidth < 8) {
   957                                  return 8;
   958                              }
   959                              return calcWidth;
   960                          })
   961                          .on("mouseenter", (ann) => {
   962                              if (!scope.showAnnotations) {
   963                                  return;
   964                              }
   965                              if (ann) {
   966                                  scope.annotation = ann;
   967                                  drawAnnLegend();
   968                              }
   969                              scope.$apply();
   970                          })
   971                          .on("click", () => {
   972                              if (!scope.showAnnotations) {
   973                                  return;
   974                              }
   975                              angular.element('#modalShower').trigger('click');
   976                          });
   977                      annotations.exit().remove();
   978                  }
   979                  var queries = paths.selectAll('.line')
   980                      .data(scope.data, (d) => { return d.Name; });
   981                  switch (scope.generator) {
   982                      case 'area':
   983                          queries.enter()
   984                              .append('path')
   985                              .attr('stroke', (d: any) => { return color(d.Name); })
   986                              .attr('class', 'line')
   987                              .style('fill', (d: any) => { return color(d.Name); });
   988                          break;
   989                      default:
   990                          queries.enter()
   991                              .append('path')
   992                              .attr('stroke', (d: any) => { return color(d.Name); })
   993                              .attr('class', 'line');
   994                  }
   995                  queries.exit()
   996                      .remove();
   997  
   998                  queries
   999                      .attr('d', (d: any) => { return line(d.Data); })
  1000                      .attr('transform', null)
  1001                      .transition()
  1002                      .ease('linear')
  1003                      .attr('transform', 'translate(' + (xScale(oldx) - xScale(xdomain[1])) + ')');
  1004                  chart.select('.x.brush')
  1005                      .call(brush)
  1006                      .selectAll('rect')
  1007                      .attr('height', height)
  1008                      .on('mouseover', () => {
  1009                          hover.style('display', 'block');
  1010                      })
  1011                      .on('mouseout', () => {
  1012                          hover.style('display', 'none');
  1013                      })
  1014                      .on('mousemove', mousemove);
  1015                  chart.select('.x.brush .extent')
  1016                      .style('stroke', '#fff')
  1017                      .style('fill-opacity', '.125')
  1018                      .style('shape-rendering', 'crispEdges');
  1019                  oldx = xdomain[1];
  1020                  drawLegend(valueIdx);
  1021              };
  1022              var extentStart: string;
  1023              var extentEnd: string;
  1024              var extentDiff: string;
  1025              function brushed() {
  1026                  var e: any;
  1027                  e = d3.event.sourceEvent;
  1028                  if (e.shiftKey) {
  1029                      return;
  1030                  }
  1031                  var extent = brush.extent();
  1032                  extentStart = datefmt(extent[0]);
  1033                  extentEnd = datefmt(extent[1]);
  1034                  extentDiff = fmtDuration(moment(extent[1]).diff(moment(extent[0])));
  1035                  drawLegend(valueIdx);
  1036                  if (scope.enableBrush && extentEnd != extentStart) {
  1037                      scope.brushStart = extentStart;
  1038                      scope.brushEnd = extentEnd;
  1039                      scope.$apply();
  1040                  }
  1041              }
  1042  
  1043              function annotateBrushed() {
  1044                  if (!scope.annotateEnabled) {
  1045                      return;
  1046                  }
  1047                  var e: any
  1048                  e = d3.event.sourceEvent;
  1049                  if (!e.shiftKey) {
  1050                      return;
  1051                  }
  1052                  var extent = brush.extent();
  1053                  scope.annotation = new Annotation();
  1054                  scope.annotation.StartDate = moment(extent[0]).utc().format(timeFormat);
  1055                  scope.annotation.EndDate = moment(extent[1]).utc().format(timeFormat);
  1056                  scope.$apply(); // This logs a console type error, but also works .. odd.
  1057                  angular.element('#modalShower').trigger('click');
  1058              }
  1059  
  1060              var mfmt = 'YYYY/MM/DD-HH:mm:ss';
  1061              function datefmt(d: any) {
  1062                  return moment(d).utc().format(mfmt);
  1063              }
  1064          },
  1065      };
  1066  }]);