sabisu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,893 @@
1
+ (function() {
2
+ var sabisu,
3
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
4
+
5
+ sabisu = angular.module('sabisu', []);
6
+
7
+ sabisu.config(function($locationProvider) {
8
+ return $locationProvider.html5Mode(true);
9
+ });
10
+
11
+ sabisu.filter('slice', function() {
12
+ return function(arr, start, end) {
13
+ return arr.slice(start, end);
14
+ };
15
+ });
16
+
17
+ sabisu.filter('joinBy', function() {
18
+ return function(input, delimiter) {
19
+ return (input || []).join(delimiter || ',');
20
+ };
21
+ });
22
+
23
+ sabisu.controller('eventsController', function($scope, $log, $location, $filter, $sce, eventsFactory, stashesFactory) {
24
+ $scope.first_search = true;
25
+ $scope.alt_pressed = false;
26
+ $scope.checks = [];
27
+ $scope.clients = [];
28
+ $scope.events = [];
29
+ $scope.event_fields = [];
30
+ $scope.event_fields_custom = [];
31
+ $scope.event_fields_facet = [];
32
+ $scope.event_fields_int = [];
33
+ $scope.event_fields_name = [];
34
+ $scope.events_spin = false;
35
+ $scope.showAll = false;
36
+ $scope.bulk = 'show';
37
+ $scope.isActive = true;
38
+ $scope.showDetails = [];
39
+ $scope.previous_events_ranges = {};
40
+ $scope.previous_events_counts = {};
41
+ $scope.previous_events_events = {};
42
+ $(window).on('focus', function() {
43
+ $scope.isActive = true;
44
+ $scope.updateEvents();
45
+ return $scope.changes();
46
+ });
47
+ $(window).on('blur', function() {
48
+ return $scope.isActive = false;
49
+ });
50
+ $(window).keydown(function(evt) {
51
+ if (evt.which === 18) {
52
+ return $scope.alt_pressed = true;
53
+ }
54
+ });
55
+ $(window).keyup(function(evt) {
56
+ if (evt.which === 18) {
57
+ return $scope.alt_pressed = false;
58
+ }
59
+ });
60
+ if ($location.search().query != null) {
61
+ $scope.search_field = $location.search().query;
62
+ $scope.search = $location.search().query;
63
+ } else {
64
+ $scope.search_field = '';
65
+ $scope.search = '';
66
+ }
67
+ if ($location.search().sort != null) {
68
+ $scope.sort_field = $location.search().sort;
69
+ $scope.sort = $location.search().sort;
70
+ } else {
71
+ $scope.sort = '-age';
72
+ $scope.sort_field = '-age';
73
+ }
74
+ if ($location.search().limit != null) {
75
+ $scope.limit = $location.search().limit;
76
+ $scope.limit_field = $location.search().limit;
77
+ } else {
78
+ $scope.limit = '50';
79
+ $scope.limit_field = '50';
80
+ }
81
+ if ($location.search().showAll != null) {
82
+ $scope.showAll = $location.search().showAll;
83
+ $log.info($scope.showAll);
84
+ }
85
+ $scope.updateEventFields = function() {
86
+ return eventsFactory.event_fields().success(function(data, status, headers, config) {
87
+ var defaults, field, _i, _len, _ref, _ref1, _results;
88
+ defaults = ['client', 'check', 'status', 'state_change', 'occurrence', 'issued', 'output'];
89
+ $scope.event_fields = data;
90
+ _ref = $scope.event_fields;
91
+ _results = [];
92
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
93
+ field = _ref[_i];
94
+ $scope.event_fields_name.push(field.name);
95
+ if (field.type === 'int') {
96
+ $scope.event_fields_int.push(field.name);
97
+ $scope.event_fields_int.push('-' + field.name);
98
+ }
99
+ if (field.facet === true) {
100
+ $scope.event_fields_facet.push(field.name);
101
+ }
102
+ if (_ref1 = field.name, __indexOf.call(defaults, _ref1) < 0) {
103
+ _results.push($scope.event_fields_custom.push(field));
104
+ } else {
105
+ _results.push(void 0);
106
+ }
107
+ }
108
+ return _results;
109
+ }).error(function(data, status, headers, config) {
110
+ return alert("Failed to get fields");
111
+ });
112
+ };
113
+ $scope.updateStashes = function() {
114
+ return stashesFactory.stashes().success(function(data, status, headers, config) {
115
+ var check, client, event, parts, stash, stashes, _base, _base1, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
116
+ stashes = [];
117
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
118
+ stash = data[_i];
119
+ if (stash['path'].match(/^silence\//)) {
120
+ stashes.push(stash);
121
+ }
122
+ }
123
+ $scope.stashes = stashes;
124
+ _ref = $scope.stashes;
125
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
126
+ stash = _ref[_j];
127
+ parts = stash['path'].split('/', 3);
128
+ client = parts[1];
129
+ if (parts.length > 2) {
130
+ check = parts[2];
131
+ } else {
132
+ check = null;
133
+ }
134
+ _ref1 = $scope.events;
135
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
136
+ event = _ref1[_k];
137
+ if ((_base = event.client).silenced == null) {
138
+ _base.silenced = false;
139
+ }
140
+ if ((_base1 = event.check).silenced == null) {
141
+ _base1.silenced = false;
142
+ }
143
+ if (client === event.client.name) {
144
+ if (check === null) {
145
+ event.client.silenced = true;
146
+ event.client.silence_stash = stash;
147
+ break;
148
+ } else {
149
+ if (check === event.check.name) {
150
+ event.check.silenced = true;
151
+ event.check.silence_stash = stash;
152
+ break;
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ $('.close_popover').click(function() {
159
+ return $scope.closePopovers();
160
+ });
161
+ $('body').on('click', function(e) {
162
+ return $('[data-toggle="popover"]').each(function() {
163
+ if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
164
+ return $(this).popover('hide');
165
+ }
166
+ });
167
+ });
168
+ return $('.glyphicon-question-sign').tooltip();
169
+ });
170
+ };
171
+ $scope.closePopovers = function() {
172
+ return $('.silenceBtn').popover('hide');
173
+ };
174
+ $scope.createSilenceDetails = function(client, check) {
175
+ if (check == null) {
176
+ check = null;
177
+ }
178
+ $scope.silencePath = client;
179
+ if (check) {
180
+ return $scope.silencePath += '/' + check;
181
+ }
182
+ };
183
+ $scope.updateSilenceDetails = function(stash) {
184
+ var check, client, parts;
185
+ parts = stash['path'].split('/', 3);
186
+ client = parts[1];
187
+ check = null;
188
+ if (parts.length > 2) {
189
+ check = parts[2];
190
+ }
191
+ $scope.silencePath = client;
192
+ if (check) {
193
+ $scope.silencePath += '/' + check;
194
+ }
195
+ if (stash['content']['timestamp']) {
196
+ $scope.silenceCreated = $filter('date')(stash['content']['timestamp'] * 1000, 'short');
197
+ }
198
+ $scope.silenceOwner = stash['content']['owner'];
199
+ if (stash['content']['expiration'] === 'resolve') {
200
+ $scope.silenceExpires = 'On resolve';
201
+ $scope.silenceExpirationClass = 'success';
202
+ } else if (stash['content']['expiration'] === 'never') {
203
+ $scope.silenceExpires = 'Never';
204
+ $scope.silenceExpirationClass = 'danger';
205
+ } else if ((stash['expire'] != null) && stash['expire'] !== -1) {
206
+ $scope.silenceExpires = moment.unix(parseInt(stash['content']['timestamp']) + parseInt(stash['expire'])).fromNow();
207
+ $scope.silenceExpirationClass = 'warning';
208
+ }
209
+ if (stash['content']['reason'] != null) {
210
+ return $scope.silenceReason = stash['content']['reason'];
211
+ }
212
+ };
213
+ $scope.saveSilence = function() {
214
+ var expiration, owner, re, reason, stash, timerToSec, timer_val, valid;
215
+ valid = true;
216
+ owner = $('#owner').val();
217
+ if (owner === '') {
218
+ $('.silence_owner').removeClass('has-success');
219
+ $('.silence_owner').addClass('has-error');
220
+ valid = false;
221
+ } else {
222
+ $('.silence_owner').removeClass('has-error');
223
+ $('.silence_owner').addClass('has-success');
224
+ }
225
+ reason = $('#reason').val();
226
+ if (reason === '') {
227
+ $('.silence_reason').removeClass('has-success');
228
+ $('.silence_reason').addClass('has-error');
229
+ valid = false;
230
+ } else {
231
+ $('.silence_reason').removeClass('has-error');
232
+ $('.silence_reason').addClass('has-success');
233
+ }
234
+ timer_val = $('#timer_val').val();
235
+ expiration = $('input[name=expiration]:checked', '#silence_form').val();
236
+ if (expiration === 'timer') {
237
+ re = new RegExp('^\\d*(m|h|d|w)$');
238
+ if (re.test(timer_val)) {
239
+ $('.silence_timer_val').removeClass('has-error');
240
+ $('.silence_timer_val').addClass('has-success');
241
+ } else {
242
+ $('.silence_timer_val').removeClass('has-success');
243
+ $('.silence_timer_val').addClass('has-error');
244
+ valid = false;
245
+ }
246
+ } else {
247
+ $('.silence_timer_val').removeClass('has-error');
248
+ $('.silence_timer_val').removeClass('has-success');
249
+ }
250
+ timerToSec = function(val) {
251
+ var conversion, q, quantity, u, unit;
252
+ q = new RegExp('^\\d*');
253
+ u = new RegExp('[a-z]$');
254
+ conversion = {
255
+ m: 60,
256
+ h: 60 * 60,
257
+ d: 60 * 60 * 24,
258
+ w: 60 * 60 * 24 * 7
259
+ };
260
+ quantity = val.match(q)[0];
261
+ unit = val.match(u)[0];
262
+ return quantity * conversion[unit];
263
+ };
264
+ if (valid) {
265
+ stash = {};
266
+ stash['path'] = "silence/" + $scope.silencePath;
267
+ stash['content'] = {};
268
+ stash['content']['timestamp'] = Math.round((new Date().getTime()) / 1000);
269
+ stash['content']['owner'] = owner;
270
+ stash['content']['reason'] = reason;
271
+ stash['content']['expiration'] = expiration;
272
+ if (expiration === 'timer') {
273
+ stash['expire'] = timerToSec(timer_val);
274
+ }
275
+ return stashesFactory.saveStash(stash).success(function(data, status, headers, config) {
276
+ $scope.updateStashes();
277
+ owner = $('#owner').val();
278
+ $('.silence_owner').removeClass('has-success');
279
+ $('.silence_owner').removeClass('has-error');
280
+ reason = $('#reason').val();
281
+ $('.silence_reason').removeClass('has-success');
282
+ $('.silence_reason').removeClass('has-error');
283
+ timer_val = $('#timer_val').val();
284
+ expiration = $('input[name=expiration]:checked', '#silence_form').val();
285
+ $('.silence_timer_val').removeClass('has-error');
286
+ $('.silence_timer_val').removeClass('has-success');
287
+ return $('#silence_window').modal('hide');
288
+ }).error(function(data, status, headers, config) {
289
+ return alert("Failed to silence: (" + status + ") " + data);
290
+ });
291
+ }
292
+ };
293
+ $scope.deleteSilence = function(path) {
294
+ return stashesFactory.deleteStash(path).success(function(data, status, headers, config) {
295
+ $scope.updateStashes();
296
+ return $scope.closePopovers();
297
+ }).error(function(data, status, headers, config) {
298
+ return alert("Failed to delete silence");
299
+ });
300
+ };
301
+ $scope.resolveEvent = function(client, check) {
302
+ return eventsFactory.resolveEvent(client, check).success(function(data, status, headers, config) {
303
+ return $scope.updateEvents();
304
+ }).error(function(data, status, headers, config) {
305
+ return alert("Faild to resolve event: " + client + "/" + check);
306
+ });
307
+ };
308
+ $scope.updateParams = function() {
309
+ $scope.search = $scope.search_field;
310
+ $scope.sort = $scope.sort_field;
311
+ $scope.limit = $scope.limit_field;
312
+ $location.search('query', $scope.search);
313
+ $location.search('sort', $scope.sort);
314
+ $location.search('limit', $scope.limit);
315
+ return $scope.updateEvents();
316
+ };
317
+ $scope.format_attr_value = function(event, key, path) {
318
+ var field, p, val, _i, _j, _len, _len1, _ref;
319
+ path = path.split('.');
320
+ val = event;
321
+ for (_i = 0, _len = path.length; _i < _len; _i++) {
322
+ p = path[_i];
323
+ if (p in val) {
324
+ val = val[p];
325
+ } else {
326
+ val = null;
327
+ break;
328
+ }
329
+ }
330
+ if (val === void 0 || val === null) {
331
+ val = 'n/a';
332
+ } else if ($scope.typeIsArray(val)) {
333
+ val = $filter('joinBy')(val, ', ');
334
+ } else if (String(val).match('^[0-9]{13}$')) {
335
+ val = $filter('date')(val, 'short');
336
+ } else {
337
+ _ref = $scope.event_fields;
338
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
339
+ field = _ref[_j];
340
+ if (field.name === key && field.type === 'url') {
341
+ val = "<a href='" + val + "'>goto</a>";
342
+ }
343
+ }
344
+ }
345
+ return $sce.trustAsHtml(String(val));
346
+ };
347
+ $scope.getEventAttributes = function(event) {
348
+ var a, attr, i, side, _i, _j, _len, _len1, _ref, _ref1;
349
+ attr = {
350
+ 'left': [['issued', 'check.issued'], ['interval', 'check.interval'], ['occurrences', 'occurrences']].concat((function() {
351
+ var _i, _len, _ref, _results;
352
+ _ref = $scope.event_fields_custom;
353
+ _results = [];
354
+ for (_i = 0, _len = _ref.length; _i < _len; _i += 2) {
355
+ i = _ref[_i];
356
+ _results.push([i.name, i.path]);
357
+ }
358
+ return _results;
359
+ })()),
360
+ 'right': [['state change', 'rel_time'], ['subscribers', 'check.subscribers'], ['handlers', 'check.handlers']].concat((function() {
361
+ var _i, _len, _ref, _results;
362
+ _ref = $scope.event_fields_custom.slice(1);
363
+ _results = [];
364
+ for (_i = 0, _len = _ref.length; _i < _len; _i += 2) {
365
+ i = _ref[_i];
366
+ _results.push([i.name, i.path]);
367
+ }
368
+ return _results;
369
+ })())
370
+ };
371
+ _ref = ['left', 'right'];
372
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
373
+ side = _ref[_i];
374
+ _ref1 = attr[side];
375
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
376
+ a = _ref1[_j];
377
+ a[1] = $scope.format_attr_value(event, a[0], a[1]);
378
+ }
379
+ }
380
+ return attr;
381
+ };
382
+ $scope.appendQuery = function(val, type, quote) {
383
+ var q;
384
+ if (type == null) {
385
+ type = null;
386
+ }
387
+ if (quote == null) {
388
+ quote = true;
389
+ }
390
+ q = '';
391
+ if ($scope.search.length > 0) {
392
+ if ($scope.alt_pressed) {
393
+ q += ' AND NOT ';
394
+ } else {
395
+ q += ' AND ';
396
+ }
397
+ } else {
398
+ if ($scope.alt_pressed) {
399
+ q += '*:* AND NOT ';
400
+ }
401
+ }
402
+ if (type != null) {
403
+ q += type + ':';
404
+ }
405
+ if (quote) {
406
+ q += "\"" + val + "\"";
407
+ } else {
408
+ q += "" + val;
409
+ }
410
+ $scope.search += q;
411
+ $scope.search_field = $scope.search;
412
+ $location.search('query', $scope.search);
413
+ return $scope.updateEvents();
414
+ };
415
+ $scope.updateEvents = function() {
416
+ if ($scope.first_search) {
417
+ $scope.events_spin = true;
418
+ }
419
+ $scope.first_search = false;
420
+ return eventsFactory.searchEvents($scope.search, $scope.sort, $scope.limit, $scope.event_fields_int).success(function(data, status, headers, config) {
421
+ var check, client, color, event, events, field, id, k, parts, stash, stats, statuses, v, _base, _base1, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
422
+ color = ['success', 'warning', 'danger', 'info'];
423
+ status = ['OK', 'Warning', 'Critical', 'Unknown'];
424
+ events = [];
425
+ if ('bookmark' in data) {
426
+ $scope.bookmark = data['bookmark'];
427
+ }
428
+ if ('count' in data) {
429
+ $scope.count = data['count'];
430
+ }
431
+ if ('ranges' in data && !angular.equals($scope.previous_events_ranges, data['ranges']['status'])) {
432
+ statuses = data['ranges']['status'];
433
+ $scope.previous_events_ranges = statuses;
434
+ $('#stats_status').find('#totals').find('.label-warning').text("Warning: " + statuses['Warning']);
435
+ $('#stats_status').find('#totals').find('.label-danger').text("Critical: " + statuses['Critical']);
436
+ $('#stats_status').find('#totals').find('.label-info').text("Unknown: " + statuses['Unknown']);
437
+ }
438
+ if ('counts' in data && !angular.equals($scope.previous_events_counts, data['counts'])) {
439
+ $scope.previous_events_counts = data['counts'];
440
+ stats = {};
441
+ for (field in data['counts']) {
442
+ stats[field] = [];
443
+ _ref = data['counts'][field];
444
+ for (k in _ref) {
445
+ v = _ref[k];
446
+ stats[field].push([k, v]);
447
+ }
448
+ stats[field].sort(function(a, b) {
449
+ return a[1] - b[1];
450
+ }).reverse();
451
+ }
452
+ $scope.stats = stats;
453
+ }
454
+ if ('rows' in data && !angular.equals($scope.previous_events_events, data.rows)) {
455
+ $scope.previous_events_events = angular.copy(data.rows);
456
+ _ref1 = data.rows;
457
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
458
+ event = _ref1[_i];
459
+ event = event.doc.event;
460
+ id = "" + event.client.name + "/" + event.check.name;
461
+ event.id = CryptoJS.MD5(id).toString(CryptoJS.enc.Base64);
462
+ if ((_ref2 = event.id, __indexOf.call($scope.showDetails, _ref2) >= 0) || $scope.showAll === 'true') {
463
+ event.showdetails = 'in';
464
+ } else {
465
+ event.showdetails = '';
466
+ }
467
+ event.color = color[event.check.status];
468
+ event.wstatus = status[event.check.status];
469
+ if (event.check.state_change === null || event.check.state_change === void 0) {
470
+ event.rel_time = 'n/a';
471
+ } else {
472
+ event.rel_time = moment.unix(event.check.state_change).fromNow();
473
+ }
474
+ event.check.issued = event.check.issued * 1000;
475
+ if (event.check.state_change != null) {
476
+ event.check.state_change = event.check.state_change * 1000;
477
+ }
478
+ if ((_base = event.client).silenced == null) {
479
+ _base.silenced = false;
480
+ }
481
+ if ((_base1 = event.check).silenced == null) {
482
+ _base1.silenced = false;
483
+ }
484
+ if ($scope.stashes != null) {
485
+ _ref3 = $scope.stashes;
486
+ for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
487
+ stash = _ref3[_j];
488
+ parts = stash['path'].split('/', 3);
489
+ client = parts[1];
490
+ if (parts.length > 2) {
491
+ check = parts[2];
492
+ } else {
493
+ check = null;
494
+ }
495
+ if (client === event.client.name) {
496
+ if (check === null) {
497
+ event.client.silenced = true;
498
+ event.client.silence_stash = stash;
499
+ } else if (check === event.check.name) {
500
+ event.check.silenced = true;
501
+ event.check.silence_stash = stash;
502
+ }
503
+ }
504
+ }
505
+ }
506
+ event.attributes = $scope.getEventAttributes(event);
507
+ events.push(event);
508
+ }
509
+ $scope.events_spin = false;
510
+ if (!angular.equals($scope.events, events)) {
511
+ $scope.events = events;
512
+ $scope.updateStashes();
513
+ }
514
+ }
515
+ $scope.events_spin = false;
516
+ return $('#corner_status').text("Last Update: " + $filter('date')(Date.now(), 'mediumTime'));
517
+ });
518
+ };
519
+ $scope.updateEventFields();
520
+ $scope.updateEvents();
521
+ $scope.changes = function() {
522
+ var params;
523
+ $log.info("STARTING _CHANGES FEED");
524
+ params = {
525
+ feed: 'longpoll',
526
+ heartbeat: 10000
527
+ };
528
+ if ($scope.last_seq != null) {
529
+ params['since'] = $scope.last_seq;
530
+ return eventsFactory.changes(params).success(function(data, status, headers, config) {
531
+ $scope.last_seq = data['last_seq'];
532
+ $scope.updateEvents();
533
+ if ($scope.isActive === true) {
534
+ return $scope.changes();
535
+ }
536
+ }).error(function(data, status, headers, config) {
537
+ $log.error("failed changes request (" + status + ") - " + data);
538
+ if ($scope.isActive === true) {
539
+ return $scope.changes();
540
+ }
541
+ });
542
+ }
543
+ };
544
+ $scope.get_sequence = function() {
545
+ return eventsFactory.last_sequence().success(function(data, status, headers, config) {
546
+ $scope.last_seq = data['last_seq'];
547
+ $log.info($scope.last_seq);
548
+ return $scope.changes();
549
+ });
550
+ };
551
+ $scope.get_sequence();
552
+ $scope.bulkToggleDetails = function() {
553
+ var action, event, _i, _j, _len, _len1, _ref, _ref1, _results;
554
+ if ($scope.bulk === 'show') {
555
+ action = 'show';
556
+ $scope.showDetails = [];
557
+ _ref = $scope.events;
558
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
559
+ event = _ref[_i];
560
+ $scope.showDetails.push(event.id);
561
+ }
562
+ } else {
563
+ action = 'hide';
564
+ $scope.showDetails = [];
565
+ }
566
+ _ref1 = $scope.events;
567
+ _results = [];
568
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
569
+ event = _ref1[_j];
570
+ _results.push($("#" + event.id).collapse(action));
571
+ }
572
+ return _results;
573
+ };
574
+ $('.collapse').on('hide.bs.collapse', function() {
575
+ if ($scope.showDetails.length === 0) {
576
+ return $scope.bulk = 'show';
577
+ }
578
+ });
579
+ $('.collapse').on('show.bs.collapse', function() {
580
+ if ($scope.showDetails.length > 0) {
581
+ return $scope.bulk = 'hide';
582
+ }
583
+ });
584
+ $scope.toggleDetails = function(id) {
585
+ var i;
586
+ if (!$("#" + id).hasClass('in')) {
587
+ $("#" + id).collapse('show');
588
+ if ($scope.showDetails.indexOf(id) === -1) {
589
+ $scope.showDetails.push(id);
590
+ }
591
+ $("#" + id).parent().find('.toggleBtnIcon').removeClass('glyphicon-collapse-down');
592
+ return $("#" + id).parent().find('.toggleBtnIcon').addClass('glyphicon-collapse-up');
593
+ } else {
594
+ $("#" + id).collapse('hide');
595
+ i = $scope.showDetails.indexOf(id);
596
+ if (i !== -1) {
597
+ $scope.showDetails.splice(i, 1);
598
+ }
599
+ $("#" + id).parent().find('.toggleBtnIcon').removeClass('glyphicon-collapse-up');
600
+ return $("#" + id).parent().find('.toggleBtnIcon').addClass('glyphicon-collapse-down');
601
+ }
602
+ };
603
+ $scope.togglePopover = function() {
604
+ $(this).popover();
605
+ return $(this).popover('toggle');
606
+ };
607
+ $scope.typeIsArray = function(value) {
608
+ return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length'));
609
+ };
610
+
611
+ /* Keyboard Shortcuts */
612
+ Mousetrap.bind('?', function() {
613
+ $log.info('showing shortcuts');
614
+ return $("#keyboard_shortcuts").modal('show');
615
+ }, 'keyup');
616
+ Mousetrap.bind('.', function() {
617
+ return $('#search_input').focus();
618
+ }, 'keyup');
619
+ Mousetrap.bind('s', function() {
620
+ $('#sort').focus();
621
+ return $('#sort').click();
622
+ }, 'keyup');
623
+ return Mousetrap.bind('enter', function() {
624
+ return $scope.updateParams();
625
+ }, 'keyup');
626
+ });
627
+
628
+ sabisu.directive('searchTypeahead', function($log, $window, $filter, $timeout, eventsFactory) {
629
+ return function(scope, element, attrs) {
630
+ var all_but_last_clause, at_bool, at_key, at_none, at_val, clause_char_list, current_clause, el, whitespace_at_end;
631
+ clause_char_list = "a-zA-Z0-9_\\-\\?\\*\\+\\~\\.\\[\\]\\{\\}\\^\"";
632
+ el = angular.element(element);
633
+ current_clause = function() {
634
+ var lastIndex;
635
+ lastIndex = el.current_search_string.lastIndexOf(" ");
636
+ return el.current_search_string.substring(lastIndex + 1).split(':');
637
+ };
638
+ all_but_last_clause = function() {
639
+ var a, lastIndex;
640
+ lastIndex = el.current_search_string.lastIndexOf(" ");
641
+ a = el.current_search_string.substring(0, lastIndex);
642
+ if (a.length > 0) {
643
+ a = a + ' ';
644
+ }
645
+ return a;
646
+ };
647
+ whitespace_at_end = function() {
648
+ var m;
649
+ m = el.current_search_string.match(RegExp(" $"));
650
+ return m != null;
651
+ };
652
+ at_none = function() {
653
+ var c, quote_count;
654
+ c = current_clause();
655
+ quote_count = 0;
656
+ if (c.length >= 2) {
657
+ quote_count = c[1].split('"').length - 1;
658
+ }
659
+ return el.current_search_string.slice(-1) === '"' && quote_count === 2;
660
+ };
661
+ at_val = function() {
662
+ return (current_clause() != null) && current_clause().length === 2 && !whitespace_at_end();
663
+ };
664
+ at_bool = function() {
665
+ if (all_but_last_clause().match(RegExp("(AND|AND NOT|OR|OR NOT) $"))) {
666
+ return false;
667
+ }
668
+ if (all_but_last_clause() === '') {
669
+ return false;
670
+ }
671
+ if ((current_clause() != null) && current_clause().length === 2 && whitespace_at_end()) {
672
+ return true;
673
+ }
674
+ if (current_clause() !== null) {
675
+ if ('AND NOT'.match(RegExp("^" + (current_clause()[0]))) != null) {
676
+ return true;
677
+ }
678
+ if ('OR NOT'.match(RegExp("^" + (current_clause()[0]))) != null) {
679
+ return true;
680
+ }
681
+ }
682
+ return false;
683
+ };
684
+ at_key = function() {
685
+ return !at_bool() && !at_val() && !at_none();
686
+ };
687
+ el.typeahead({
688
+ minLength: 0,
689
+ highlight: true,
690
+ hint: true
691
+ }, {
692
+ name: 'keys',
693
+ displayKey: 'name',
694
+ source: function(search_string, cb) {
695
+ var data, dd, indent, key, search_word, search_word_clean, sel_start;
696
+ el.current_search_string = search_string;
697
+ search_word = current_clause() ? current_clause().slice(-1)[0] : '';
698
+ if (at_key() && whitespace_at_end()) {
699
+ search_word = '';
700
+ }
701
+ search_word_clean = search_word.trim().replace(/"/, '');
702
+ sel_start = element[0].selectionStart || 0;
703
+ if (whitespace_at_end()) {
704
+ indent = Math.max(0, sel_start) * 0.535;
705
+ } else {
706
+ if (current_clause().length > 1) {
707
+ key = current_clause()[0].length + 1;
708
+ } else {
709
+ key = 0;
710
+ }
711
+ indent = Math.max(0, sel_start - (search_word.length + key)) * 0.535;
712
+ }
713
+ dd = angular.element("#" + element[0].id + " ~ .tt-dropdown-menu");
714
+ dd[0].style.left = "" + indent + "em";
715
+ if (search_string.length === 0) {
716
+ angular.element(".tt-hint").hide();
717
+ } else {
718
+ angular.element(".tt-hint").show();
719
+ }
720
+ if (at_val()) {
721
+ key = current_clause()[0];
722
+ if (scope.stats[key]) {
723
+ data = [];
724
+ angular.forEach(scope.stats[key], function(v, k) {
725
+ if (v[0].trim() !== "" && v[0].indexOf(search_word_clean) === 0) {
726
+ return data.push({
727
+ name: "" + key + ":" + v[0]
728
+ });
729
+ }
730
+ });
731
+ data = $filter('orderBy')(data, 'name');
732
+ return cb(data);
733
+ } else {
734
+ return cb([]);
735
+ }
736
+ } else if (at_bool()) {
737
+ return cb([
738
+ {
739
+ name: 'AND'
740
+ }, {
741
+ name: 'AND NOT'
742
+ }, {
743
+ name: 'OR'
744
+ }, {
745
+ name: 'OR NOT'
746
+ }
747
+ ]);
748
+ } else if (at_none()) {
749
+ return cb([]);
750
+ } else {
751
+ return eventsFactory.event_fields().success(function(data, status, headers, config) {
752
+ var field, fields, _i, _len;
753
+ fields = [];
754
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
755
+ field = data[_i];
756
+ if (field.index) {
757
+ fields.push(field);
758
+ }
759
+ }
760
+ if (search_word_clean.length > 0) {
761
+ fields = $.grep(fields, function(n, i) {
762
+ return n.name.indexOf(search_word_clean) === 0;
763
+ });
764
+ }
765
+ fields = $filter('orderBy')(fields, 'name');
766
+ return cb(fields);
767
+ });
768
+ }
769
+ }
770
+ });
771
+ el.on('focus', function() {
772
+ var curval;
773
+ curval = scope.search_field;
774
+ el.typeahead('val', 'c').typeahead('open');
775
+ return el.typeahead('val', curval).typeahead('open');
776
+ });
777
+ el.on('typeahead:selected', function($e, datum) {
778
+ var val;
779
+ if (at_val()) {
780
+ val = datum.name.split(':');
781
+ el.typeahead('val', all_but_last_clause() + val[0] + ':"' + val[1] + '" ');
782
+ } else if (at_key()) {
783
+ el.typeahead('val', all_but_last_clause() + datum.name + ':');
784
+ } else {
785
+ el.typeahead('val', all_but_last_clause() + datum.name + ' ');
786
+ }
787
+ scope.search_field = el.typeahead('val');
788
+ return $timeout(function() {
789
+ var curval;
790
+ curval = el.typeahead('val');
791
+ el.typeahead('val', 'c').typeahead('open');
792
+ return el.typeahead('val', curval).typeahead('open');
793
+ }, 100);
794
+ });
795
+ el.on('typeahead:autocompleted', function($e, datum) {
796
+ var val;
797
+ if (at_key()) {
798
+ el.typeahead('val', all_but_last_clause() + datum.name + ':');
799
+ } else if (at_val()) {
800
+ val = datum.name.split(':');
801
+ el.typeahead('val', all_but_last_clause() + val[0] + ':"' + val[1] + '" ');
802
+ } else {
803
+ el.typeahead('val', all_but_last_clause() + datum.name + ' ');
804
+ }
805
+ scope.search_field = el.typeahead('val');
806
+ return el.typeahead('open');
807
+ });
808
+ return el.on('typeahead:cursorchanged', function($e, datum, dsName) {
809
+ angular.element(".tt-input").val(all_but_last_clause() + datum.name);
810
+ return angular.element(".tt-hint").val("");
811
+ });
812
+ };
813
+ });
814
+
815
+ sabisu.factory('eventsFactory', function($log, $http) {
816
+ var factory;
817
+ factory = {};
818
+ factory.searchEvents = function(search_query, sort, limit, ints) {
819
+ if (sort === 'age') {
820
+ sort = 'state_change';
821
+ }
822
+ if (sort === '-age') {
823
+ sort = '-state_change';
824
+ }
825
+ if (__indexOf.call(ints, sort) < 0) {
826
+ sort = sort + '<string>';
827
+ }
828
+ sort = "[\"" + sort + "\"]";
829
+ if (search_query === '') {
830
+ search_query = '*:*';
831
+ }
832
+ return $http({
833
+ method: 'GET',
834
+ url: '/api/events/search',
835
+ params: {
836
+ query: search_query,
837
+ limit: limit,
838
+ sort: sort
839
+ }
840
+ });
841
+ };
842
+ factory.resolveEvent = function(client, check) {
843
+ return $http({
844
+ method: 'POST',
845
+ url: '/sensu/resolve',
846
+ data: {
847
+ client: client,
848
+ check: check
849
+ }
850
+ });
851
+ };
852
+ factory.changes = function(params) {
853
+ return $http({
854
+ method: 'GET',
855
+ url: '/api/events/changes',
856
+ params: params
857
+ });
858
+ };
859
+ factory.last_sequence = function() {
860
+ return $http({
861
+ method: 'GET',
862
+ url: '/api/events/changes',
863
+ params: {
864
+ limit: 1,
865
+ descending: true
866
+ }
867
+ });
868
+ };
869
+ factory.event_fields = function() {
870
+ return $http({
871
+ method: 'GET',
872
+ url: '/api/configuration/fields'
873
+ });
874
+ };
875
+ return factory;
876
+ });
877
+
878
+ sabisu.factory('stashesFactory', function($log, $http) {
879
+ var factory;
880
+ factory = {};
881
+ factory.stashes = function() {
882
+ return $http.get('/sensu/stashes');
883
+ };
884
+ factory.saveStash = function(stash) {
885
+ return $http.post("/sensu/stashes", stash);
886
+ };
887
+ factory.deleteStash = function(path) {
888
+ return $http["delete"]("/sensu/stashes/" + path);
889
+ };
890
+ return factory;
891
+ });
892
+
893
+ }).call(this);