Cleanup index.html (#4587)
* Minor doc update * Minor documentation update * Removed registry.html A never-finished attempt to extract registry functionality from index.html * Fixed makefile * Extracted index.html javascript code Applied automatic formatting, and a minor fix to make it work. * More automatic code formatting * Extracted CSS from index.html #4586 * Minor * Applied automatic formatting * Properly style the loadOverlay * Handle theme param in URL hash * Use relative path for main.js and main.css
George Moschovitis committed
Nov 8, 2018 at 21:30 UTC
3d05855462f5f00a174e38f22df94f0b901e48a9
7 files changed
+4847
-4699
web/gui/Makefile.am
+2
-1
@@ -59,10 +59,11 @@ dist_web_DATA = \
59
favicon.ico \
60
goto-host-from-alarm.html \
61
index.html \
62
+ main.css \
63
+ main.js \
64
infographic.html \
65
robots.txt \
66
refresh-badges.js \
65
- registry.html \
67
sitemap.xml \
68
tv.html \
69
version.txt \
web/gui/README.md
+2
-2
@@ -2,11 +2,11 @@
2
3
## Generating dashboard.js
4
5
-The monolithic `dashboards.js` file is automatically generated by concatenating the source files located in the `web/gui/src` directory by running the build script:
5
+The monolithic `dashboards.js` file is automatically generated by concatenating the source files located in the `web/gui/src/dashboard.js/` directory by running the build script:
6
7
```sh
8
cd web/gui
9
-./build-dashboard-js.sh
9
+make
10
```
11
12
After every change in the `src` directory, the `dashboard.js` file should be regenerated and commited to the repository.
web/gui/dashboard_info.js
+3
-3
@@ -2,10 +2,10 @@
2
3
var netdataDashboard = window.netdataDashboard || {};
4
5
-// ----------------------------------------------------------------------------
6
-// menus
5
+// Informational content for the various sections of the GUI (menus, sections, charts, etc.)
6
8
-// information about the main menus
7
+// ----------------------------------------------------------------------------
8
+// Menus
9
10
netdataDashboard.menu = {
11
'system': {
web/gui/index.html
+14
-4490
@@ -13,6 +13,8 @@
13
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
14
<meta name="author" content="costa@tsaousis.gr">
15
16
+ <link rel="stylesheet" type="text/css" href="main.css">
17
+
18
<!-- <link rel="shortcut icon" href="images/seo-performance-multi-size.ico"> -->
19
20
<!-- <link rel="apple-touch-icon" href="images/seo-performance-72.png"> -->
@@ -48,4494 +50,7 @@
50
<meta name="twitter:description" content="Unparalleled insights, in real-time, of everything happening on your Linux systems and applications, with stunning, interactive web dashboards and powerful performance and health alarms." />
51
<meta name="twitter:image" content="https://cloud.githubusercontent.com/assets/2662304/14092712/93b039ea-f551-11e5-822c-beadbf2b2a2e.gif" />
52
51
- <style>
52
-
53
- /* force the vertical window scrollbar */
54
- html {
55
- overflow-y: scroll;
56
- }
57
-
58
- /* prevent body from hiding under the navbar */
59
- body {
60
- padding-top: 50px;
61
- }
62
-
63
- .loadOverlay {
64
- position: absolute;
65
- top: 0px;
66
- left: 0px;
67
- width: 100%;
68
- height:100%;
69
- z-index: 2000;
70
- font-size: 10vh;
71
- font-family: sans-serif;
72
- padding: 40vh 0 40vh 0;
73
- font-weight: bold;
74
- text-align: center;
75
- }
76
-
77
- .navbar-highlight {
78
- display: none;
79
- position: fixed;
80
- margin-top: 5px;
81
- height: 26px;
82
- width: 100%;
83
- text-align: center;
84
- overflow: hidden;
85
- z-index: 30;
86
- pointer-events: none !important;
87
- }
88
-
89
- .navbar-highlight-content {
90
- position: relative;
91
- display: inline-block;
92
- margin: 0 auto;
93
- height: 26px;
94
- min-width: 500px;
95
- background-color:rgba(0, 0, 0, 0.7);
96
- padding-top: 2px;
97
- padding-bottom: 2px;
98
- padding-left: 15px;
99
- padding-right: 15px;
100
- border-radius:10px;
101
- color: lightgrey;
102
- pointer-events: auto !important;
103
- }
104
-
105
- .navbar-highlight-bar {
106
- cursor: pointer;
107
- }
108
- .navbar-highlight-button-right {
109
- cursor: pointer;
110
- padding-left: 10px;
111
- }
112
-
113
- .modal-wide .modal-dialog {
114
- width: 80%;
115
- }
116
-
117
- /* fix # anchors scrolling under the navbar
118
- https://github.com/twbs/bootstrap/issues/1768#issuecomment-46519033
119
- */
120
- h1 {
121
- position: relative;
122
- z-index: -1;
123
- }
124
- h2 {
125
- position: relative;
126
- z-index: -2;
127
- }
128
- h1:before, h2:before {
129
- display: block;
130
- content: " ";
131
- margin-top: -70px;
132
- height: 70px;
133
- visibility: hidden;
134
- }
135
-
136
- .p {
137
- display: block;
138
- margin-top: 15px;
139
- }
140
-
141
- .option-row,
142
- .option-control {
143
- vertical-align: top;
144
- padding: 10px;
145
- padding-top: 30px;
146
- padding-left: 30px;
147
- }
148
-
149
- .option-info {
150
- padding: 10px;
151
- }
152
-
153
- .dashboard-submenu-info {
154
- display: block;
155
- margin-top: 10px;
156
- }
157
-
158
- .dashboard-context-info {
159
- display: block;
160
- margin-top: 10px;
161
- }
162
-
163
- #masthead h1 {
164
- /*font-size: 30px;*/
165
- line-height: 1;
166
- padding-top: 30px;
167
- }
168
-
169
- #masthead .well {
170
- margin-top:4%;
171
- }
172
-
173
- /* fix the navbar shifting when a modal is open */
174
- /* https://github.com/twbs/bootstrap/issues/14040#issuecomment-159891033 */
175
- body.modal-open{
176
- width: 100% !important;
177
- padding-right: 0 !important;
178
-/* overflow-y: scroll !important; */
179
-/* position: fixed !important;*/
180
- overflow: visible;
181
- }
182
-
183
- /* make accordion use the whole header bar for expand/collapse */
184
- .panel-title a {
185
- display: block;
186
- padding: 10px 15px;
187
- margin: -10px -15px;
188
- }
189
-
190
- /*
191
- * Side navigation
192
- *
193
- * Scrollspy and affixed enhanced navigation to highlight sections and secondary
194
- * sections of docs content.
195
- */
196
-
197
- .affix {
198
- position: static;
199
- top: 70px !important;
200
- /*width: 220px;*/
201
- }
202
-
203
- .affix-top {
204
- /*width: 220px;*/
205
- }
206
-
207
- .dashboard-sidebar {
208
- max-height: calc(100% - 70px) !important;
209
- overflow-y: auto;
210
- /*width: 220px !important;*/
211
- }
212
-
213
- /* By default it's not affixed in mobile views, so undo that */
214
- .dashboard-sidebar.affix {
215
- position: static;
216
- }
217
-
218
- @media (min-width: 768px) {
219
- .dashboard-sidebar {
220
- padding-left: 20px;
221
- }
222
- }
223
-
224
- /* First level of nav */
225
- .dashboard-sidenav {
226
- margin-top: 20px;
227
- margin-bottom: 20px;
228
- }
229
-
230
- /* All levels of nav */
231
- .dashboard-sidebar .nav > li > a {
232
- display: block;
233
- padding: 4px 20px;
234
- font-size: 13px;
235
- font-weight: 500;
236
- color: #767676;
237
- }
238
- .dashboard-sidebar .nav > li > a > .svg-inline--fa {
239
- width: 20px;
240
- text-align: center;
241
- }
242
- .dashboard-sidebar .nav > li > a:hover,
243
- .dashboard-sidebar .nav > li > a:focus {
244
- padding-left: 19px;
245
- color: #563d7c;
246
- text-decoration: none;
247
- background-color: transparent;
248
- border-left: 1px solid #563d7c;
249
- }
250
- .dashboard-sidebar .nav > .active > a,
251
- .dashboard-sidebar .nav > .active:hover > a,
252
- .dashboard-sidebar .nav > .active:focus > a {
253
- padding-left: 18px;
254
- font-weight: bold;
255
- color: #563d7c;
256
- background-color: transparent;
257
- border-left: 2px solid #563d7c;
258
- }
259
-
260
- /* Nav: second level (shown on .active) */
261
- .dashboard-sidebar .nav .nav {
262
- display: none; /* Hide by default, but at >768px, show it */
263
- padding-bottom: 10px;
264
- }
265
- .dashboard-sidebar .nav .nav > li > a {
266
- padding-top: 1px;
267
- padding-bottom: 1px;
268
- padding-left: 30px;
269
- font-size: 12px;
270
- font-weight: normal;
271
- }
272
- .dashboard-sidebar .nav .nav > li > a:hover,
273
- .dashboard-sidebar .nav .nav > li > a:focus {
274
- padding-left: 29px;
275
- }
276
- .dashboard-sidebar .nav .nav > .active > a,
277
- .dashboard-sidebar .nav .nav > .active:hover > a,
278
- .dashboard-sidebar .nav .nav > .active:focus > a {
279
- padding-left: 28px;
280
- font-weight: 500;
281
- }
282
-
283
- .dropdown-menu {
284
- min-width: 200px;
285
- }
286
- .dropdown-menu.columns-2 {
287
- margin: 0;
288
- padding: 0;
289
- width: 400px;
290
- }
291
- .dropdown-menu li a {
292
- padding: 5px 15px;
293
- font-weight: 300;
294
- }
295
- .dropdown-menu.multi-column {
296
- overflow-x: hidden;
297
- }
298
- .multi-column-dropdown {
299
- list-style: none;
300
- padding: 0;
301
- }
302
- .multi-column-dropdown li a {
303
- display: block;
304
- clear: both;
305
- line-height: 1.428571429;
306
- white-space: normal;
307
- }
308
- .multi-column-dropdown li a:hover {
309
- text-decoration: none;
310
- color: #f5f5f5;
311
- background-color: #262626;
312
- }
313
- .scrollable-menu {
314
- height: auto;
315
- max-height: 80vh;
316
- overflow-x: hidden;
317
- }
318
- .scrollable-menu-50 {
319
- height: auto;
320
- max-height: 50vh;
321
- overflow-x: hidden;
322
- }
323
-
324
- /* Back to top (hidden on mobile) */
325
- .back-to-top,
326
- .dashboard-theme-toggle {
327
- display: none;
328
- padding: 4px 10px;
329
- margin-top: 10px;
330
- margin-left: 10px;
331
- font-size: 12px;
332
- font-weight: 500;
333
- color: #999;
334
- }
335
- .back-to-top:hover,
336
- .dashboard-theme-toggle:hover {
337
- color: #563d7c;
338
- text-decoration: none;
339
- }
340
- .dashboard-theme-toggle {
341
- margin-top: 0;
342
- }
343
-
344
- .container {
345
- width: calc(100% - 20px) !important;
346
- }
347
-
348
- .charts-body {
349
- display: inline-block;
350
- width: 100%;
351
- }
352
-
353
- .sidebar-body {
354
- position: absolute;
355
- display: none;
356
- }
357
-
358
- .dashboard-section-container {
359
- display: block;
360
- width: 100%;
361
- page-break-before: auto;
362
- page-break-after: auto;
363
- page-break-inside: auto;
364
- }
365
-
366
- .dashboard-print-row {
367
- display: block;
368
- width: 100%;
369
- page-break-before: auto;
370
- page-break-after: auto;
371
- page-break-inside: avoid;
372
- }
373
-
374
- .netdata-chartblock-container {
375
- display: inline-block;
376
- }
377
-
378
- /* https://github.com/seiyria/bootstrap-slider/issues/746 */
379
- .tooltip {
380
- -webkit-user-select: none;
381
- -moz-user-select: none;
382
- -ms-user-select: none;
383
- user-select: none;
384
- }
385
-
386
- @media print {
387
- body {
388
- overflow: visible !important;
389
- -webkit-print-color-adjust: exact;
390
- page-break-inside: auto;
391
- page-break-before: auto;
392
- page-break-after: auto;
393
- }
394
-
395
- .dashboard-section {
396
- page-break-inside: auto;
397
- page-break-before: auto;
398
- page-break-after: auto;
399
- }
400
-
401
- .dashboard-subsection {
402
- page-break-before: avoid;
403
- page-break-after: auto;
404
- page-break-inside: auto;
405
- }
406
-
407
- .charts-body {
408
- padding-left: 0%;
409
- padding-right: 0%;
410
- display: block;
411
- page-break-inside: auto;
412
- page-break-before: auto;
413
- page-break-after: auto;
414
- }
415
-
416
- .back-to-top,
417
- .dashboard-theme-toggle {
418
- display: block;
419
- }
420
- }
421
-
422
- @media (min-width: 768px) {
423
- .charts-body {
424
- padding-left: 0%;
425
- padding-right: 0%;
426
- }
427
-
428
- .back-to-top,
429
- .dashboard-theme-toggle {
430
- display: block;
431
- }
432
- }
433
-
434
- /* Show and affix the side nav when space allows it */
435
- @media (min-width: 992px) {
436
- .container {
437
- padding-left: 0% !important;
438
- }
439
-
440
- .charts-body {
441
- width: calc(100% - 213px) !important;
442
- padding-left: 1% !important;
443
- padding-right: 0% !important;
444
- }
445
-
446
- .sidebar-body {
447
- display: inline-block !important;
448
- width: 213px !important;
449
- }
450
-
451
- .dashboard-sidebar .nav > .active > ul {
452
- display: block;
453
- }
454
-
455
- /* Widen the fixed sidebar */
456
- .dashboard-sidebar.affix,
457
- .dashboard-sidebar.affix-top,
458
- .dashboard-sidebar.affix-bottom {
459
- width: 213px !important;
460
- }
461
- .dashboard-sidebar.affix {
462
- position: fixed; /* Undo the static from mobile first approach */
463
- top: 20px;
464
- }
465
- .dashboard-sidebar.affix-bottom {
466
- position: absolute; /* Undo the static from mobile first approach */
467
- }
468
- .dashboard-sidebar.affix-bottom .dashboard-sidenav,
469
- .dashboard-sidebar.affix .dashboard-sidenav {
470
- margin-top: 0;
471
- margin-bottom: 0;
472
- }
473
- }
474
-
475
- @media (min-width: 1200px) {
476
- .container {
477
- padding-left: 2% !important;
478
- }
479
-
480
- .charts-body {
481
- width: calc(100% - 233px) !important;
482
- padding-left: 1% !important;
483
- padding-right: 1% !important;
484
- }
485
-
486
- .sidebar-body {
487
- display: inline-block !important;
488
- width: 233px !important;
489
- }
490
-
491
- /* Widen the fixed sidebar again */
492
- .dashboard-sidebar.affix,
493
- .dashboard-sidebar.affix-top,
494
- .dashboard-sidebar.affix-bottom {
495
- width: 233px !important;
496
- }
497
- }
498
-
499
- @media (min-width: 1360px) {
500
- .container {
501
- padding-left: 3% !important;
502
- }
503
-
504
- .charts-body {
505
- width: calc(100% - 263px) !important;
506
- padding-left: 1% !important;
507
- padding-right: 2% !important;
508
- }
509
-
510
- .sidebar-body {
511
- display: inline-block !important;
512
- width: 263px !important;
513
- }
514
-
515
- /* Widen the fixed sidebar again */
516
- .dashboard-sidebar.affix,
517
- .dashboard-sidebar.affix-top,
518
- .dashboard-sidebar.affix-bottom {
519
- width: 263px !important;
520
- }
521
- }
522
-
523
- .action-button {
524
- position: relative;
525
- display: inline-block;
526
- color: gray;
527
- cursor: pointer;
528
- margin: 0 auto;
529
- width: 30px;
530
- height: 30px;
531
- font-size: 25px;
532
- }
533
-
534
- .ripple {
535
- position: relative;
536
- /*overflow: hidden;*/
537
- transform: translate3d(0, 0, 0)
538
- }
539
-
540
- .ripple:after {
541
- content: "";
542
- display: block;
543
- position: absolute;
544
- width: 100%;
545
- height: 100%;
546
- top: 0;
547
- left: 0;
548
- pointer-events: none;
549
- background-image: radial-gradient(circle, #000 10%, transparent 10.01%);
550
- background-repeat: no-repeat;
551
- background-position: 50%;
552
- transform: scale(18, 18); /* the size of the ripple */
553
- opacity: 0;
554
- transition: transform .5s, opacity 1s
555
- }
556
-
557
- .ripple:active:after {
558
- transform: scale(0, 0);
559
- opacity: .2;
560
- transition: 0s
561
- }
562
- </style>
563
-
564
- <!-- check which theme to use -->
565
- <script type="text/javascript">
566
- // netdata snapshot data
567
- var netdataSnapshotData = null;
568
-
569
- // enable alarms checking and notifications
570
- var netdataShowAlarms = true;
571
-
572
- // enable registry updates
573
- var netdataRegistry = true;
574
-
575
- // forward definition only - not used here
576
- var netdataServer = undefined;
577
- var netdataServerStatic = undefined;
578
- var netdataCheckXSS = undefined;
579
-
580
- // control the welcome modal and analytics
581
- var this_is_demo = null;
582
-
583
- function escapeUserInputHTML(s) {
584
- return s.toString()
585
- .replace(/&/g, '&')
586
- .replace(/</g, '<')
587
- .replace(/>/g, '>')
588
- .replace(/"/g, '"')
589
- .replace(/#/g, '#')
590
- .replace(/'/g, ''')
591
- .replace(/\(/g,'(')
592
- .replace(/\)/g,')')
593
- .replace(/\//g,'/');
594
- }
595
-
596
- function verifyURL(s) {
597
- if(typeof(s) === 'string' && (s.startsWith('http://') || s.startsWith('https://')))
598
- return s
599
- .replace(/'/g, '%22')
600
- .replace(/"/g, '%27')
601
- .replace(/\)/g, '%28')
602
- .replace(/\(/g, '%29');
603
-
604
- console.log('invalid URL detected:');
605
- console.log(s);
606
- return 'javascript:alert("invalid url");';
607
- }
608
-
609
- // --------------------------------------------------------------------
610
- // urlOptions
611
-
612
- var urlOptions = {
613
- hash: '#',
614
- theme: null,
615
- help: null,
616
- mode: 'live', // 'live', 'print'
617
- update_always: false,
618
- pan_and_zoom: false,
619
- server: null,
620
- after: 0,
621
- before: 0,
622
- highlight: false,
623
- highlight_after: 0,
624
- highlight_before: 0,
625
- nowelcome: false,
626
- show_alarms: false,
627
- chart: null,
628
- family: null,
629
- alarm: null,
630
- alarm_unique_id: 0,
631
- alarm_id: 0,
632
- alarm_event_id: 0,
633
-
634
- hasProperty: function(property) {
635
- // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
636
- return typeof this[property] !== 'undefined';
637
- },
638
-
639
- genHash: function(forReload) {
640
- var hash = urlOptions.hash;
641
-
642
- if(urlOptions.pan_and_zoom === true) {
643
- hash += ';after=' + urlOptions.after.toString() +
644
- ';before=' + urlOptions.before.toString();
645
- }
646
-
647
- if(urlOptions.highlight === true) {
648
- hash += ';highlight_after=' + urlOptions.highlight_after.toString() +
649
- ';highlight_before=' + urlOptions.highlight_before.toString();
650
- }
651
-
652
- if(urlOptions.theme !== null)
653
- hash += ';theme=' + urlOptions.theme.toString();
654
-
655
- if(urlOptions.help !== null)
656
- hash += ';help=' + urlOptions.help.toString();
657
-
658
- if(urlOptions.update_always === true)
659
- hash += ';update_always=true';
660
-
661
- if(forReload === true && urlOptions.server !== null)
662
- hash += ';server=' + urlOptions.server.toString();
663
-
664
- if(urlOptions.mode !== 'live')
665
- hash += ';mode=' + urlOptions.mode;
666
-
667
- return hash;
668
- },
669
-
670
- parseHash: function() {
671
- var variables = document.location.hash.split(';');
672
- var len = variables.length;
673
- while(len--) {
674
- if(len !== 0) {
675
- var p = variables[len].split('=');
676
- if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
677
- urlOptions[p[0]] = decodeURIComponent(p[1]);
678
- }
679
- else {
680
- if(variables[len].length > 0)
681
- urlOptions.hash = variables[len];
682
- }
683
- }
684
-
685
- var booleans = [ 'nowelcome', 'show_alarms', 'update_always' ];
686
- len = booleans.length;
687
- while(len--) {
688
- if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1)
689
- urlOptions[booleans[len]] = true;
690
- else
691
- urlOptions[booleans[len]] = false;
692
- }
693
-
694
- var numeric = [ 'after', 'before', 'highlight_after', 'highlight_before' ];
695
- len = numeric.length;
696
- while(len--) {
697
- if(typeof urlOptions[numeric[len]] === 'string') {
698
- try {
699
- urlOptions[numeric[len]] = parseInt(urlOptions[numeric[len]]);
700
- }
701
- catch(e) {
702
- console.log('failed to parse URL hash parameter ' + numeric[len]);
703
- urlOptions[numeric[len]] = 0;
704
- }
705
- }
706
- }
707
-
708
- if(urlOptions.server !== null && urlOptions.server !== '') {
709
- netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString();
710
- netdataServer = urlOptions.server;
711
- netdataCheckXSS = true;
712
- }
713
- else
714
- urlOptions.server = null;
715
-
716
- if(urlOptions.before > 0 && urlOptions.after > 0) {
717
- urlOptions.pan_and_zoom = true;
718
- urlOptions.nowelcome = true;
719
- }
720
- else
721
- urlOptions.pan_and_zoom = false;
722
-
723
- if(urlOptions.highlight_before > 0 && urlOptions.highlight_after > 0) {
724
- urlOptions.highlight = true;
725
- }
726
- else
727
- urlOptions.highlight = false;
728
-
729
- switch(urlOptions.mode) {
730
- case 'print':
731
- urlOptions.theme = 'white';
732
- urlOptions.welcome = false;
733
- urlOptions.help = false;
734
- urlOptions.show_alarms = false;
735
-
736
- if(urlOptions.pan_and_zoom === false) {
737
- urlOptions.pan_and_zoom = true;
738
- urlOptions.before = Date.now();
739
- urlOptions.after = urlOptions.before - 600000;
740
- }
741
-
742
- netdataShowAlarms = false;
743
- netdataRegistry = false;
744
- this_is_demo = false;
745
- break;
746
-
747
- case 'live':
748
- default:
749
- urlOptions.mode = 'live';
750
- break;
751
- }
752
-
753
- // console.log(urlOptions);
754
- },
755
-
756
- hashUpdate: function() {
757
- history.replaceState(null, '', urlOptions.genHash(true));
758
- },
759
-
760
- netdataPanAndZoomCallback: function(status, after, before) {
761
- //console.log(1);
762
- //console.log(new Error().stack);
763
-
764
- if(netdataSnapshotData === null) {
765
- urlOptions.pan_and_zoom = status;
766
- urlOptions.after = after;
767
- urlOptions.before = before;
768
- urlOptions.hashUpdate();
769
- }
770
- },
771
-
772
- netdataHighlightCallback: function(status, after, before) {
773
- //console.log(2);
774
- //console.log(new Error().stack);
775
-
776
- if(status === true && (after === null || before === null || after <= 0 || before <= 0 || after >= before)) {
777
- status = false;
778
- after = 0;
779
- before = 0;
780
- }
781
-
782
- if(netdataSnapshotData === null)
783
- urlOptions.highlight = status;
784
- else
785
- urlOptions.highlight = false;
786
-
787
- urlOptions.highlight_after = Math.round(after);
788
- urlOptions.highlight_before = Math.round(before);
789
- urlOptions.hashUpdate();
790
-
791
- var show_eye = NETDATA.globalChartUnderlay.hasViewport();
792
-
793
- if(status === true && after > 0 && before > 0 && after < before) {
794
- var d1 = NETDATA.dateTime.localeDateString(after);
795
- var d2 = NETDATA.dateTime.localeDateString(before);
796
- if(d1 === d2) d2 = '';
797
- document.getElementById('navbar-highlight-content').innerHTML =
798
- ((show_eye === true)?'<span class="navbar-highlight-bar highlight-tooltip" onclick="urlOptions.showHighlight();" title="restore the highlighted view" data-toggle="tooltip" data-placement="bottom">':'<span>').toString()
799
- + 'highlighted time-frame'
800
- + ' <b>' + d1 + ' <code>' + NETDATA.dateTime.localeTimeString(after) + '</code></b> to '
801
- + ' <b>' + d2 + ' <code>' + NETDATA.dateTime.localeTimeString(before) + '</code></b>, '
802
- + 'duration <b>' + NETDATA.seconds4human(Math.round((before - after) / 1000)) + '</b>'
803
- + '</span>'
804
- + '<span class="navbar-highlight-button-right highlight-tooltip" onclick="urlOptions.clearHighlight();" title="clear the highlighted time-frame" data-toggle="tooltip" data-placement="bottom"><i class="fas fa-times"></i></span>';
805
-
806
- $('.navbar-highlight').show();
807
-
808
- $('.highlight-tooltip').tooltip({
809
- html: true,
810
- delay: {show: 500, hide: 0},
811
- container: 'body'
812
- });
813
- }
814
- else
815
- $('.navbar-highlight').hide();
816
- },
817
-
818
- clearHighlight: function() {
819
- NETDATA.globalChartUnderlay.clear();
820
-
821
- if(NETDATA.globalPanAndZoom.isActive() === true)
822
- NETDATA.globalPanAndZoom.clearMaster();
823
- },
824
-
825
- showHighlight: function() {
826
- NETDATA.globalChartUnderlay.focus();
827
- }
828
- };
829
-
830
- urlOptions.parseHash();
831
-
832
- // --------------------------------------------------------------------
833
- // check options that should be processed before loading netdata.js
834
-
835
- var localStorageTested = -1;
836
- function localStorageTest() {
837
- if(localStorageTested !== -1)
838
- return localStorageTested;
839
-
840
- if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
841
- var test = 'test';
842
- try {
843
- localStorage.setItem(test, test);
844
- localStorage.removeItem(test);
845
- localStorageTested = true;
846
- }
847
- catch (e) {
848
- console.log(e);
849
- localStorageTested = false;
850
- }
851
- }
852
- else
853
- localStorageTested = false;
854
-
855
- return localStorageTested;
856
- }
857
-
858
- function loadLocalStorage(name) {
859
- var ret = null;
860
-
861
- try {
862
- if(localStorageTest() === true)
863
- ret = localStorage.getItem(name);
864
- else
865
- console.log('localStorage is not available');
866
- }
867
- catch(error) {
868
- console.log(error);
869
- return null;
870
- }
871
-
872
- if(typeof ret === 'undefined' || ret === null)
873
- return null;
874
-
875
- // console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
876
-
877
- return ret;
878
- }
879
-
880
- function saveLocalStorage(name, value) {
881
- // console.log('saving: ' + name.toString() + ' = ' + value.toString());
882
- try {
883
- if(localStorageTest() === true) {
884
- localStorage.setItem(name, value.toString());
885
- return true;
886
- }
887
- }
888
- catch(error) {
889
- console.log(error);
890
- }
891
-
892
- return false;
893
- }
894
-
895
- function getTheme(def) {
896
- if(urlOptions.mode === 'print')
897
- return 'white';
898
-
899
- var ret = loadLocalStorage('netdataTheme');
900
- if(typeof ret === 'undefined' || ret === null || ret === 'undefined')
901
- return def;
902
- else
903
- return ret;
904
- }
905
-
906
- function setTheme(theme) {
907
- if(urlOptions.mode === 'print') return false;
908
-
909
- if(theme === netdataTheme) return false;
910
- return saveLocalStorage('netdataTheme', theme);
911
- }
912
-
913
- var netdataTheme = getTheme('slate');
914
- var netdataShowHelp = true;
915
-
916
- if(urlOptions.theme !== null) {
917
- setTheme(urlOptions.theme);
918
- netdataTheme = urlOptions.theme;
919
- }
920
- else
921
- urlOptions.theme = netdataTheme;
922
-
923
- if(urlOptions.help !== null) {
924
- saveLocalStorage('options.show_help', urlOptions.help);
925
- netdataShowHelp = urlOptions.help;
926
- }
927
- else {
928
- urlOptions.help = loadLocalStorage('options.show_help');
929
- }
930
-
931
- // --------------------------------------------------------------------
932
- // natural sorting
933
- // http://www.davekoelle.com/files/alphanum.js - LGPL
934
-
935
- function naturalSortChunkify(t) {
936
- var tz = [];
937
- var x = 0, y = -1, n = 0, i, j;
938
-
939
- while (i = (j = t.charAt(x++)).charCodeAt(0)) {
940
- var m = (i >= 48 && i <= 57);
941
- if (m !== n) {
942
- tz[++y] = "";
943
- n = m;
944
- }
945
- tz[y] += j;
946
- }
947
-
948
- return tz;
949
- }
950
-
951
- function naturalSortCompare(a, b) {
952
- var aa = naturalSortChunkify(a.toLowerCase());
953
- var bb = naturalSortChunkify(b.toLowerCase());
954
-
955
- for (var x = 0; aa[x] && bb[x]; x++) {
956
- if (aa[x] !== bb[x]) {
957
- var c = Number(aa[x]), d = Number(bb[x]);
958
- if (c.toString() === aa[x] && d.toString() === bb[x])
959
- return c - d;
960
- else
961
- return (aa[x] > bb[x]) ? 1 : -1;
962
- }
963
- }
964
-
965
- return aa.length - bb.length;
966
- }
967
-
968
- // --------------------------------------------------------------------
969
- // saving files to client
970
-
971
- function saveTextToClient(data, filename) {
972
- var blob = new Blob( [ data ], {
973
- type: 'application/octet-stream'
974
- });
975
-
976
- var url = URL.createObjectURL( blob );
977
- var link = document.createElement( 'a' );
978
- link.setAttribute( 'href', url );
979
- link.setAttribute( 'download', filename );
980
-
981
- var el = document.getElementById('hiddenDownloadLinks');
982
- el.innerHTML = '';
983
- el.appendChild(link);
984
-
985
- setTimeout(function(){
986
- el.removeChild(link);
987
- URL.revokeObjectURL(url);
988
- }, 60);
989
-
990
- link.click();
991
- }
992
-
993
- function saveObjectToClient(data, filename) {
994
- saveTextToClient(JSON.stringify(data), filename);
995
- }
996
-
997
- // --------------------------------------------------------------------
998
- // registry call back to render my-netdata menu
999
-
1000
- var netdataRegistryCallback = function(machines_array) {
1001
- var el = '';
1002
- var a1 = '';
1003
- var found = 0, hosted = 0;
1004
- var len, i, url, hostname, icon;
1005
-
1006
- if(options.hosts.length > 1) {
1007
- // there are mirrored hosts here
1008
-
1009
- el += '<li><a href="#" onClick="return false;" style="color: #666;" target="_blank">databases available on this host</a></li>';
1010
- a1 += '<li><a href="#" onClick="return false;"><i class="fas fa-info-circle" style="color: #666;"></i></a></li>';
1011
-
1012
- var base = document.location.origin.toString() + document.location.pathname.toString();
1013
- if(base.endsWith("/host/" + options.hostname + "/"))
1014
- base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length);
1015
-
1016
- if(base.endsWith("/"))
1017
- base = base.substring(0, base.length - 1);
1018
-
1019
- var master = options.hosts[0].hostname;
1020
- var sorted = options.hosts.sort(function(a, b) {
1021
- if(a.hostname === master) return -1;
1022
- return naturalSortCompare(a.hostname, b.hostname);
1023
- });
1024
-
1025
- i = 0;
1026
- len = sorted.length;
1027
- while(len--) {
1028
- hostname = sorted[i].hostname;
1029
- if(hostname === master) {
1030
- url = base + "/";
1031
- icon = "home";
1032
- }
1033
- else {
1034
- url = base + "/host/" + hostname + "/";
1035
- icon = "window-restore";
1036
- }
1037
-
1038
- el += '<li id="registry_server_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '#" onClick="return gotoHostedModalHandler(\'' + url + '\');">' + hostname + '</a></li>';
1039
- a1 += '<li id="registry_action_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '#" onClick="return gotoHostedModalHandler(\'' + url + '\');"><i class="fas fa-' + icon + '" style="color: #999;"></i></a></li>';
1040
- hosted++;
1041
- i++;
1042
- }
1043
-
1044
- el += '<li role="separator" class="divider"></li>';
1045
- a1 += '<li role="separator" class="divider"></li>';
1046
- }
1047
-
1048
- if(machines_array === null) {
1049
- var ret = loadLocalStorage("registryCallback");
1050
- if(typeof ret !== 'undefined' && ret !== null) {
1051
- machines_array = JSON.parse(ret);
1052
- console.log("failed to contact the registry - loaded registry data from browser local storage");
1053
- }
1054
- }
1055
-
1056
- if(machines_array) {
1057
- saveLocalStorage("registryCallback", JSON.stringify(machines_array));
1058
-
1059
- var machines = machines_array.sort(function (a, b) {
1060
- return naturalSortCompare(a.name, b.name);
1061
- });
1062
-
1063
- i = 0;
1064
- len = machines.length;
1065
- while(len--) {
1066
- var u = machines[i++];
1067
- found++;
1068
- el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '#" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>';
1069
- a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fas fa-trash" style="color: #999;"></i></a></li>';
1070
- }
1071
- }
1072
-
1073
- if(!found) {
1074
- if(machines)
1075
- el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
1076
- else
1077
- el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
1078
-
1079
- a1 += '<li><a href="#" onClick="return false;"> </a></li>';
1080
-
1081
- el += '<li role="separator" class="divider"></li>' +
1082
- '<li><a href="//london.netdata.rocks/default.html">UK - London (DigitalOcean.com)</a></li>' +
1083
- '<li><a href="//newyork.netdata.rocks/default.html">US - New York (DigitalOcean.com)</a></li>' +
1084
- '<li><a href="//sanfrancisco.netdata.rocks/default.html">US - San Francisco (DigitalOcean.com)</a></li>' +
1085
- '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
1086
- '<li><a href="//frankfurt.netdata.rocks/default.html">Germany - Frankfurt (DigitalOcean.com)</a></li>' +
1087
- '<li><a href="//toronto.netdata.rocks/default.html">Canada - Toronto (DigitalOcean.com)</a></li>' +
1088
- '<li><a href="//singapore.netdata.rocks/default.html">Japan - Singapore (DigitalOcean.com)</a></li>' +
1089
- '<li><a href="//bangalore.netdata.rocks/default.html">India - Bangalore (DigitalOcean.com)</a></li>';
1090
- a1 += '<li role="separator" class="divider"></li>' +
1091
- '<li><a href="#"> </a></li>' +
1092
- '<li><a href="#"> </a></li>'+
1093
- '<li><a href="#"> </a></li>'+
1094
- '<li><a href="#"> </a></li>'+
1095
- '<li><a href="#"> </a></li>'+
1096
- '<li><a href="#"> </a></li>'+
1097
- '<li><a href="#"> </a></li>'+
1098
- '<li><a href="#"> </a></li>';
1099
- }
1100
-
1101
- el += '<li role="separator" class="divider"></li>';
1102
- a1 += '<li role="separator" class="divider"></li>';
1103
-
1104
- el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #999;" target="_blank">What is this?</a></li>';
1105
- a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fas fa-cog" style="color: #999;"></i></a></li>';
1106
-
1107
- document.getElementById('mynetdata_servers').innerHTML = el;
1108
- document.getElementById('mynetdata_servers2').innerHTML = el;
1109
- document.getElementById('mynetdata_actions1').innerHTML = a1;
1110
-
1111
- gotoServerInit();
1112
- };
1113
-
1114
- function isdemo() {
1115
- if(this_is_demo !== null) return this_is_demo;
1116
- this_is_demo = false;
1117
-
1118
- try {
1119
- if(typeof document.location.hostname === 'string') {
1120
- if(document.location.hostname.endsWith('.my-netdata.io') ||
1121
- document.location.hostname.endsWith('.mynetdata.io') ||
1122
- document.location.hostname.endsWith('.netdata.rocks') ||
1123
- document.location.hostname.endsWith('.netdata.ai') ||
1124
- document.location.hostname.endsWith('.netdata.live') ||
1125
- document.location.hostname.endsWith('.firehol.org') ||
1126
- document.location.hostname.endsWith('.netdata.online') ||
1127
- document.location.hostname.endsWith('.netdata.cloud'))
1128
- this_is_demo = true;
1129
- }
1130
- }
1131
- catch(error) {}
1132
- return this_is_demo;
1133
- }
1134
-
1135
- function netdataURL(url, forReload) {
1136
- if(typeof url === 'undefined')
1137
- // url = document.location.toString();
1138
- url = '';
1139
-
1140
- if(url.indexOf('#') !== -1)
1141
- url = url.substring(0, url.indexOf('#'));
1142
-
1143
- var hash = urlOptions.genHash(forReload);
1144
-
1145
- // console.log('netdataURL: ' + url + hash);
1146
-
1147
- return url + hash;
1148
- }
1149
-
1150
- function netdataReload(url) {
1151
- document.location = verifyURL(netdataURL(url, true));
1152
-
1153
- // since we play with hash
1154
- // this is needed to reload the page
1155
- location.reload();
1156
- }
1157
-
1158
- function gotoHostedModalHandler(url) {
1159
- document.location = verifyURL(url + urlOptions.genHash());
1160
- return false;
1161
- }
1162
-
1163
- var gotoServerValidateRemaining = 0;
1164
- var gotoServerMiddleClick = false;
1165
- var gotoServerStop = false;
1166
- function gotoServerValidateUrl(id, guid, url) {
1167
- var penalty = 0;
1168
- var error = 'failed';
1169
-
1170
- if(document.location.toString().startsWith('http://') && url.toString().startsWith('https://'))
1171
- // we penalize https only if the current url is http
1172
- // to allow the user walk through all its servers.
1173
- penalty = 500;
1174
-
1175
- else if(document.location.toString().startsWith('https://') && url.toString().startsWith('http://'))
1176
- error = 'can\'t check';
1177
-
1178
- var finalURL = netdataURL(url);
1179
-
1180
- setTimeout(function() {
1181
- document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + verifyURL(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
1182
-
1183
- NETDATA.registry.hello(url, function(data) {
1184
- if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) {
1185
- // console.log('OK ' + id + ' URL: ' + url);
1186
- document.getElementById(guid + '-' + id + '-status').innerHTML = "OK";
1187
-
1188
- if(!gotoServerStop) {
1189
- gotoServerStop = true;
1190
-
1191
- if(gotoServerMiddleClick) {
1192
- window.open(verifyURL(finalURL), '_blank');
1193
- gotoServerMiddleClick = false;
1194
- document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + verifyURL(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
1195
- }
1196
- else {
1197
- document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML(url) + '</small>';
1198
- document.location = verifyURL(finalURL);
1199
- }
1200
- }
1201
- }
1202
- else {
1203
- if(typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid !== guid)
1204
- error = 'wrong machine';
1205
-
1206
- document.getElementById(guid + '-' + id + '-status').innerHTML = error;
1207
- gotoServerValidateRemaining--;
1208
- if(gotoServerValidateRemaining <= 0) {
1209
- gotoServerMiddleClick = false;
1210
- document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>';
1211
- }
1212
- }
1213
- });
1214
- }, (id * 50) + penalty);
1215
- }
1216
-
1217
- function gotoServerModalHandler(guid) {
1218
- // console.log('goto server: ' + guid);
1219
-
1220
- gotoServerStop = false;
1221
- var checked = {};
1222
- var len = NETDATA.registry.machines[guid].alternate_urls.length;
1223
- var count = 0;
1224
-
1225
- document.getElementById('gotoServerResponse').innerHTML = '';
1226
- document.getElementById('gotoServerList').innerHTML = '';
1227
- document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name;
1228
- $('#gotoServerModal').modal('show');
1229
-
1230
- gotoServerValidateRemaining = len;
1231
- while(len--) {
1232
- var url = NETDATA.registry.machines[guid].alternate_urls[len];
1233
- checked[url] = true;
1234
- gotoServerValidateUrl(count++, guid, url);
1235
- }
1236
-
1237
- setTimeout(function() {
1238
- if(gotoServerStop === false) {
1239
- document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>';
1240
- NETDATA.registry.search(guid, function(data) {
1241
- // console.log(data);
1242
- len = data.urls.length;
1243
- while(len--) {
1244
- var url = data.urls[len][1];
1245
- // console.log(url);
1246
- if(typeof checked[url] === 'undefined') {
1247
- gotoServerValidateRemaining++;
1248
- checked[url] = true;
1249
- gotoServerValidateUrl(count++, guid, url);
1250
- }
1251
- }
1252
- });
1253
- }
1254
- }, 2000);
1255
- return false;
1256
- }
1257
-
1258
- function gotoServerInit() {
1259
- $(".registry_link").on('click', function(e) {
1260
- if(e.which === 2) {
1261
- e.preventDefault();
1262
- gotoServerMiddleClick = true;
1263
- }
1264
- else {
1265
- gotoServerMiddleClick = false;
1266
- }
1267
-
1268
- return true;
1269
- });
1270
- }
1271
-
1272
- function switchRegistryModalHandler() {
1273
- document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid;
1274
- document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server;
1275
- document.getElementById('switchRegistryResponse').innerHTML = '';
1276
- $('#switchRegistryModal').modal('show');
1277
- }
1278
-
1279
- function notifyForSwitchRegistry() {
1280
- var n = document.getElementById('switchRegistryPersonGUID').value;
1281
-
1282
- if(n !== '' && n.length === 36) {
1283
- NETDATA.registry.switch(n, function(result) {
1284
- if(result !== null) {
1285
- $('#switchRegistryModal').modal('hide');
1286
- NETDATA.registry.init();
1287
- }
1288
- else {
1289
- document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>";
1290
- }
1291
- });
1292
- }
1293
- else
1294
- document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>";
1295
- }
1296
-
1297
- var deleteRegistryUrl = null;
1298
- function deleteRegistryModalHandler(guid, name, url) {
1299
- void(guid);
1300
-
1301
- deleteRegistryUrl = url;
1302
- document.getElementById('deleteRegistryServerName').innerHTML = name;
1303
- document.getElementById('deleteRegistryServerName2').innerHTML = name;
1304
- document.getElementById('deleteRegistryServerURL').innerHTML = url;
1305
- document.getElementById('deleteRegistryResponse').innerHTML = '';
1306
- $('#deleteRegistryModal').modal('show');
1307
- }
1308
-
1309
- function notifyForDeleteRegistry() {
1310
- if(deleteRegistryUrl) {
1311
- NETDATA.registry.delete(deleteRegistryUrl, function(result) {
1312
- if(result !== null) {
1313
- deleteRegistryUrl = null;
1314
- $('#deleteRegistryModal').modal('hide');
1315
- NETDATA.registry.init();
1316
- }
1317
- else {
1318
- document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>";
1319
- }
1320
- });
1321
- }
1322
- }
1323
-
1324
- var options = {
1325
- menus: {},
1326
- submenu_names: {},
1327
- data: null,
1328
- hostname: 'netdata_server', // will be overwritten by the netdata server
1329
- version: 'unknown',
1330
- hosts: [],
1331
-
1332
- duration: 0, // the default duration of the charts
1333
- update_every: 1,
1334
-
1335
- chartsPerRow: 0,
1336
- // chartsMinWidth: 1450,
1337
- chartsHeight: 180,
1338
- };
1339
-
1340
- function chartsPerRow(total) {
1341
- void(total);
1342
-
1343
- if(options.chartsPerRow === 0) {
1344
- return 1;
1345
- //var width = Math.floor(total / options.chartsMinWidth);
1346
- //if(width === 0) width = 1;
1347
- //return width;
1348
- }
1349
- else return options.chartsPerRow;
1350
- }
1351
-
1352
- function prioritySort(a, b) {
1353
- if(a.priority < b.priority) return -1;
1354
- if(a.priority > b.priority) return 1;
1355
- return naturalSortCompare(a.name, b.name);
1356
- }
1357
-
1358
- function sortObjectByPriority(object) {
1359
- var idx = {};
1360
- var sorted = [];
1361
-
1362
- for(var i in object) {
1363
- if(!object.hasOwnProperty(i)) continue;
1364
-
1365
- if(typeof idx[i] === 'undefined') {
1366
- idx[i] = object[i];
1367
- sorted.push(i);
1368
- }
1369
- }
1370
-
1371
- sorted.sort(function(a, b) {
1372
- if(idx[a].priority < idx[b].priority) return -1;
1373
- if(idx[a].priority > idx[b].priority) return 1;
1374
- return naturalSortCompare(a, b);
1375
- });
1376
-
1377
- return sorted;
1378
- }
1379
-
1380
-
1381
- // ----------------------------------------------------------------------------
1382
- // scroll to a section, without changing the browser history
1383
-
1384
- function scrollToId(hash) {
1385
- if(hash && hash !== '' && document.getElementById(hash) !== null) {
1386
- var offset = $('#' + hash).offset();
1387
- if(typeof offset !== 'undefined') {
1388
- //console.log('scrolling to ' + hash + ' at ' + offset.top.toString());
1389
- $('html, body').animate({scrollTop: offset.top - 30}, 0);
1390
- }
1391
- }
1392
-
1393
- // we must return false to prevent the default action
1394
- return false;
1395
- }
1396
-
1397
- // ----------------------------------------------------------------------------
1398
-
1399
- // user editable information
1400
- var customDashboard = {
1401
- menu: {},
1402
- submenu: {},
1403
- context: {}
1404
- };
1405
-
1406
- // netdata standard information
1407
- var netdataDashboard = {
1408
- sparklines_registry: {},
1409
- os: 'unknown',
1410
-
1411
- menu: {},
1412
- submenu: {},
1413
- context: {},
1414
-
1415
- // generate a sparkline
1416
- // used in the documentation
1417
- sparkline: function (prefix, chart, dimension, units, suffix) {
1418
- if(options.data === null || typeof options.data.charts === 'undefined')
1419
- return '';
1420
-
1421
- if(typeof options.data.charts[chart] === 'undefined')
1422
- return '';
1423
-
1424
- if(typeof options.data.charts[chart].dimensions === 'undefined')
1425
- return '';
1426
-
1427
- if(typeof options.data.charts[chart].dimensions[dimension] === 'undefined')
1428
- return '';
1429
-
1430
- var key = chart + '.' + dimension;
1431
-
1432
- if(typeof units === 'undefined')
1433
- units = '';
1434
-
1435
- if(typeof this.sparklines_registry[key] === 'undefined')
1436
- this.sparklines_registry[key] = { count: 1 };
1437
- else
1438
- this.sparklines_registry[key].count++;
1439
-
1440
- key = key + '.' + this.sparklines_registry[key].count;
1441
-
1442
- return prefix + '<div class="netdata-container" data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')' + suffix;
1443
- },
1444
-
1445
- gaugeChart: function(title, width, dimensions, colors) {
1446
- if(typeof colors === 'undefined')
1447
- colors = '';
1448
-
1449
- if(typeof dimensions === 'undefined')
1450
- dimensions = '';
1451
-
1452
- return '<div class="netdata-container" data-netdata="CHART_UNIQUE_ID"'
1453
- + ' data-dimensions="' + dimensions + '"'
1454
- + ' data-chart-library="gauge"'
1455
- + ' data-gauge-adjust="width"'
1456
- + ' data-title="' + title + '"'
1457
- + ' data-width="' + width + '"'
1458
- + ' data-before="0"'
1459
- + ' data-after="-CHART_DURATION"'
1460
- + ' data-points="CHART_DURATION"'
1461
- + ' data-colors="' + colors + '"'
1462
- + ' role="application"></div>';
1463
- },
1464
-
1465
- anyAttribute: function(obj, attr, key, def) {
1466
- if(typeof(obj[key]) !== 'undefined') {
1467
- var x = obj[key][attr];
1468
-
1469
- if(typeof(x) === 'undefined')
1470
- return def;
1471
-
1472
- if(typeof(x) === 'function') {
1473
- return x(netdataDashboard.os);
1474
- }
1475
-
1476
- return x;
1477
- }
1478
-
1479
- return def;
1480
- },
1481
-
1482
- menuTitle: function(chart) {
1483
- if(typeof chart.menu_pattern !== 'undefined') {
1484
- return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString()
1485
- + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' ');
1486
- }
1487
-
1488
- return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' ');
1489
- },
1490
-
1491
- menuIcon: function(chart) {
1492
- if(typeof chart.menu_pattern !== 'undefined')
1493
- return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '<i class="fas fa-puzzle-piece"></i>').toString();
1494
-
1495
- return this.anyAttribute(this.menu, 'icon', chart.menu, '<i class="fas fa-puzzle-piece"></i>');
1496
- },
1497
-
1498
- menuInfo: function(chart) {
1499
- if(typeof chart.menu_pattern !== 'undefined')
1500
- return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null);
1501
-
1502
- return this.anyAttribute(this.menu, 'info', chart.menu, null);
1503
- },
1504
-
1505
- menuHeight: function(chart) {
1506
- if(typeof chart.menu_pattern !== 'undefined')
1507
- return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0);
1508
-
1509
- return this.anyAttribute(this.menu, 'height', chart.menu, 1.0);
1510
- },
1511
-
1512
- submenuTitle: function(menu, submenu) {
1513
- var key = menu + '.' + submenu;
1514
- // console.log(key);
1515
- var title = this.anyAttribute(this.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');
1516
- if(title.length > 28) {
1517
- var a = title.substring(0, 13);
1518
- var b = title.substring(title.length - 12, title.length);
1519
- return a + '...' + b;
1520
- }
1521
- return title;
1522
- },
1523
-
1524
- submenuInfo: function(menu, submenu) {
1525
- var key = menu + '.' + submenu;
1526
- return this.anyAttribute(this.submenu, 'info', key, null);
1527
- },
1528
-
1529
- submenuHeight: function(menu, submenu, relative) {
1530
- var key = menu + '.' + submenu;
1531
- return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative;
1532
- },
1533
-
1534
- contextInfo: function(id) {
1535
- var x = this.anyAttribute(this.context, 'info', id, null);
1536
-
1537
- if(x !== null)
1538
- return '<div class="shorten dashboard-context-info netdata-chart-alignment" role="document">' + x + '</div>';
1539
- else
1540
- return '';
1541
- },
1542
-
1543
- contextValueRange: function(id) {
1544
- if(typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined')
1545
- return this.context[id].valueRange;
1546
- else
1547
- return '[null, null]';
1548
- },
1549
-
1550
- contextHeight: function(id, def) {
1551
- if(typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined')
1552
- return def * this.context[id].height;
1553
- else
1554
- return def;
1555
- },
1556
-
1557
- contextDecimalDigits: function(id, def) {
1558
- if(typeof this.context[id] !== 'undefined' && typeof this.context[id].decimalDigits !== 'undefined')
1559
- return this.context[id].decimalDigits;
1560
- else
1561
- return def;
1562
- }
1563
- };
1564
-
1565
- // ----------------------------------------------------------------------------
1566
-
1567
- // enrich the data structure returned by netdata
1568
- // to reflect our menu system and content
1569
- // TODO: this is a shame - we should fix charts naming (issue #807)
1570
- function enrichChartData(chart) {
1571
- var parts = chart.type.split('_');
1572
- var tmp = parts[0];
1573
-
1574
- switch(tmp) {
1575
- case 'ap':
1576
- case 'net':
1577
- case 'disk':
1578
- case 'statsd':
1579
- chart.menu = tmp;
1580
- break;
1581
-
1582
- case 'apache':
1583
- chart.menu = chart.type;
1584
- if(parts.length > 2 && parts[1] === 'cache')
1585
- chart.menu_pattern = tmp + '_' + parts[1];
1586
- else if(parts.length > 1)
1587
- chart.menu_pattern = tmp;
1588
- break;
1589
-
1590
- case 'bind':
1591
- chart.menu = chart.type;
1592
- if(parts.length > 2 && parts[1] === 'rndc')
1593
- chart.menu_pattern = tmp + '_' + parts[1];
1594
- else if(parts.length > 1)
1595
- chart.menu_pattern = tmp;
1596
- break;
1597
-
1598
- case 'cgroup':
1599
- chart.menu = chart.type;
1600
- if(chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/))
1601
- chart.menu_pattern = 'cgqemu';
1602
- else
1603
- chart.menu_pattern = 'cgroup';
1604
- break;
1605
-
1606
- case 'go':
1607
- chart.menu = chart.type;
1608
- if(parts.length > 2 && parts[1] === 'expvar')
1609
- chart.menu_pattern = tmp + '_' + parts[1];
1610
- else if(parts.length > 1)
1611
- chart.menu_pattern = tmp;
1612
- break;
1613
-
1614
- case 'isc':
1615
- chart.menu = chart.type;
1616
- if(parts.length > 2 && parts[1] === 'dhcpd')
1617
- chart.menu_pattern = tmp + '_' + parts[1];
1618
- else if(parts.length > 1)
1619
- chart.menu_pattern = tmp;
1620
- break;
1621
-
1622
- case 'ovpn':
1623
- chart.menu = chart.type;
1624
- if(parts.length > 3 && parts[1] === 'status' && parts[2] === 'log')
1625
- chart.menu_pattern = tmp + '_' + parts[1];
1626
- else if(parts.length > 1)
1627
- chart.menu_pattern = tmp;
1628
- break;
1629
-
1630
- case 'smartd':
1631
- case 'web':
1632
- chart.menu = chart.type;
1633
- if(parts.length > 2 && parts[1] === 'log')
1634
- chart.menu_pattern = tmp + '_' + parts[1];
1635
- else if(parts.length > 1)
1636
- chart.menu_pattern = tmp;
1637
- break;
1638
-
1639
- case 'tc':
1640
- chart.menu = tmp;
1641
-
1642
- // find a name for this device from fireqos info
1643
- // we strip '_(in|out)' or '(in|out)_'
1644
- if(chart.context === 'tc.qos' && (typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)) {
1645
- var n = chart.name.split('.')[1];
1646
- if(n.endsWith('_in'))
1647
- options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in'));
1648
- else if(n.endsWith('_out'))
1649
- options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out'));
1650
- else if(n.startsWith('in_'))
1651
- options.submenu_names[chart.family] = n.slice(3, n.length);
1652
- else if(n.startsWith('out_'))
1653
- options.submenu_names[chart.family] = n.slice(4, n.length);
1654
- else
1655
- options.submenu_names[chart.family] = n;
1656
- }
1657
-
1658
- // increase the priority of IFB devices
1659
- // to have inbound appear before outbound
1660
- if(chart.id.match(/.*-ifb$/))
1661
- chart.priority--;
1662
-
1663
- break;
1664
-
1665
- default:
1666
- chart.menu = chart.type;
1667
- if(parts.length > 1)
1668
- chart.menu_pattern = tmp;
1669
- break;
1670
- }
1671
-
1672
- chart.submenu = chart.family;
1673
- }
1674
-
1675
- // ----------------------------------------------------------------------------
1676
-
1677
- function headMain(os, charts, duration) {
1678
- void(os);
1679
-
1680
- if(urlOptions.mode === 'print')
1681
- return '';
1682
-
1683
- var head = '';
1684
-
1685
- if(typeof charts['system.swap'] !== 'undefined')
1686
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.swap"'
1687
- + ' data-dimensions="used"'
1688
- + ' data-append-options="percentage"'
1689
- + ' data-chart-library="easypiechart"'
1690
- + ' data-title="Used Swap"'
1691
- + ' data-units="%"'
1692
- + ' data-easypiechart-max-value="100"'
1693
- + ' data-width="9%"'
1694
- + ' data-before="0"'
1695
- + ' data-after="-' + duration.toString() + '"'
1696
- + ' data-points="' + duration.toString() + '"'
1697
- + ' data-colors="#DD4400"'
1698
- + ' role="application"></div>';
1699
-
1700
- if(typeof charts['system.io'] !== 'undefined') {
1701
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
1702
- + ' data-dimensions="in"'
1703
- + ' data-chart-library="easypiechart"'
1704
- + ' data-title="Disk Read"'
1705
- + ' data-width="11%"'
1706
- + ' data-before="0"'
1707
- + ' data-after="-' + duration.toString() + '"'
1708
- + ' data-points="' + duration.toString() + '"'
1709
- + ' data-common-units="system.io.mainhead"'
1710
- + ' role="application"></div>';
1711
-
1712
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
1713
- + ' data-dimensions="out"'
1714
- + ' data-chart-library="easypiechart"'
1715
- + ' data-title="Disk Write"'
1716
- + ' data-width="11%"'
1717
- + ' data-before="0"'
1718
- + ' data-after="-' + duration.toString() + '"'
1719
- + ' data-points="' + duration.toString() + '"'
1720
- + ' data-common-units="system.io.mainhead"'
1721
- + ' role="application"></div>';
1722
- }
1723
- else if(typeof charts['system.pgpgio'] !== 'undefined') {
1724
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
1725
- + ' data-dimensions="in"'
1726
- + ' data-chart-library="easypiechart"'
1727
- + ' data-title="Disk Read"'
1728
- + ' data-width="11%"'
1729
- + ' data-before="0"'
1730
- + ' data-after="-' + duration.toString() + '"'
1731
- + ' data-points="' + duration.toString() + '"'
1732
- + ' data-common-units="system.pgpgio.mainhead"'
1733
- + ' role="application"></div>';
1734
-
1735
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
1736
- + ' data-dimensions="out"'
1737
- + ' data-chart-library="easypiechart"'
1738
- + ' data-title="Disk Write"'
1739
- + ' data-width="11%"'
1740
- + ' data-before="0"'
1741
- + ' data-after="-' + duration.toString() + '"'
1742
- + ' data-points="' + duration.toString() + '"'
1743
- + ' data-common-units="system.pgpgio.mainhead"'
1744
- + ' role="application"></div>';
1745
- }
1746
-
1747
- if(typeof charts['system.cpu'] !== 'undefined')
1748
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.cpu"'
1749
- + ' data-chart-library="gauge"'
1750
- + ' data-title="CPU"'
1751
- + ' data-units="%"'
1752
- + ' data-gauge-max-value="100"'
1753
- + ' data-width="20%"'
1754
- + ' data-after="-' + duration.toString() + '"'
1755
- + ' data-points="' + duration.toString() + '"'
1756
- + ' data-colors="' + NETDATA.colors[12] + '"'
1757
- + ' role="application"></div>';
1758
-
1759
- if(typeof charts['system.net'] !== 'undefined') {
1760
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
1761
- + ' data-dimensions="received"'
1762
- + ' data-chart-library="easypiechart"'
1763
- + ' data-title="Net Inbound"'
1764
- + ' data-width="11%"'
1765
- + ' data-before="0"'
1766
- + ' data-after="-' + duration.toString() + '"'
1767
- + ' data-points="' + duration.toString() + '"'
1768
- + ' data-common-units="system.net.mainhead"'
1769
- + ' role="application"></div>';
1770
-
1771
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
1772
- + ' data-dimensions="sent"'
1773
- + ' data-chart-library="easypiechart"'
1774
- + ' data-title="Net Outbound"'
1775
- + ' data-width="11%"'
1776
- + ' data-before="0"'
1777
- + ' data-after="-' + duration.toString() + '"'
1778
- + ' data-points="' + duration.toString() + '"'
1779
- + ' data-common-units="system.net.mainhead"'
1780
- + ' role="application"></div>';
1781
- }
1782
- else if(typeof charts['system.ip'] !== 'undefined') {
1783
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
1784
- + ' data-dimensions="received"'
1785
- + ' data-chart-library="easypiechart"'
1786
- + ' data-title="IP Inbound"'
1787
- + ' data-width="11%"'
1788
- + ' data-before="0"'
1789
- + ' data-after="-' + duration.toString() + '"'
1790
- + ' data-points="' + duration.toString() + '"'
1791
- + ' data-common-units="system.ip.mainhead"'
1792
- + ' role="application"></div>';
1793
-
1794
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
1795
- + ' data-dimensions="sent"'
1796
- + ' data-chart-library="easypiechart"'
1797
- + ' data-title="IP Outbound"'
1798
- + ' data-width="11%"'
1799
- + ' data-before="0"'
1800
- + ' data-after="-' + duration.toString() + '"'
1801
- + ' data-points="' + duration.toString() + '"'
1802
- + ' data-common-units="system.ip.mainhead"'
1803
- + ' role="application"></div>';
1804
- }
1805
- else if(typeof charts['system.ipv4'] !== 'undefined') {
1806
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
1807
- + ' data-dimensions="received"'
1808
- + ' data-chart-library="easypiechart"'
1809
- + ' data-title="IPv4 Inbound"'
1810
- + ' data-width="11%"'
1811
- + ' data-before="0"'
1812
- + ' data-after="-' + duration.toString() + '"'
1813
- + ' data-points="' + duration.toString() + '"'
1814
- + ' data-common-units="system.ipv4.mainhead"'
1815
- + ' role="application"></div>';
1816
-
1817
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
1818
- + ' data-dimensions="sent"'
1819
- + ' data-chart-library="easypiechart"'
1820
- + ' data-title="IPv4 Outbound"'
1821
- + ' data-width="11%"'
1822
- + ' data-before="0"'
1823
- + ' data-after="-' + duration.toString() + '"'
1824
- + ' data-points="' + duration.toString() + '"'
1825
- + ' data-common-units="system.ipv4.mainhead"'
1826
- + ' role="application"></div>';
1827
- }
1828
- else if(typeof charts['system.ipv6'] !== 'undefined') {
1829
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
1830
- + ' data-dimensions="received"'
1831
- + ' data-chart-library="easypiechart"'
1832
- + ' data-title="IPv6 Inbound"'
1833
- + ' data-units="kbps"'
1834
- + ' data-width="11%"'
1835
- + ' data-before="0"'
1836
- + ' data-after="-' + duration.toString() + '"'
1837
- + ' data-points="' + duration.toString() + '"'
1838
- + ' data-common-units="system.ipv6.mainhead"'
1839
- + ' role="application"></div>';
1840
-
1841
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
1842
- + ' data-dimensions="sent"'
1843
- + ' data-chart-library="easypiechart"'
1844
- + ' data-title="IPv6 Outbound"'
1845
- + ' data-units="kbps"'
1846
- + ' data-width="11%"'
1847
- + ' data-before="0"'
1848
- + ' data-after="-' + duration.toString() + '"'
1849
- + ' data-points="' + duration.toString() + '"'
1850
- + ' data-common-units="system.ipv6.mainhead"'
1851
- + ' role="application"></div>';
1852
- }
1853
-
1854
- if(typeof charts['system.ram'] !== 'undefined')
1855
- head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ram"'
1856
- + ' data-dimensions="used|buffers|active|wired"' // active and wired are FreeBSD stats
1857
- + ' data-append-options="percentage"'
1858
- + ' data-chart-library="easypiechart"'
1859
- + ' data-title="Used RAM"'
1860
- + ' data-units="%"'
1861
- + ' data-easypiechart-max-value="100"'
1862
- + ' data-width="9%"'
1863
- + ' data-after="-' + duration.toString() + '"'
1864
- + ' data-points="' + duration.toString() + '"'
1865
- + ' data-colors="' + NETDATA.colors[7] + '"'
1866
- + ' role="application"></div>';
1867
-
1868
- return head;
1869
- }
1870
-
1871
- function generateHeadCharts(type, chart, duration) {
1872
- if(urlOptions.mode === 'print')
1873
- return '';
1874
-
1875
- var head = '';
1876
- var hcharts = netdataDashboard.anyAttribute(netdataDashboard.context, type, chart.context, []);
1877
- if(hcharts.length > 0) {
1878
- var hi = 0, hlen = hcharts.length;
1879
- while(hi < hlen) {
1880
- if(typeof hcharts[hi] === 'function')
1881
- head += hcharts[hi](netdataDashboard.os, chart.id).replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id);
1882
- else
1883
- head += hcharts[hi].replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id);
1884
- hi++;
1885
- }
1886
- }
1887
- return head;
1888
- }
1889
-
1890
- function renderPage(menus, data) {
1891
- var div = document.getElementById('charts_div');
1892
- var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
1893
-
1894
- // find the proper duration for per-second updates
1895
- var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
1896
- options.duration = duration;
1897
- options.update_every = data.update_every;
1898
-
1899
- var html = '';
1900
- var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">';
1901
- var mainhead = headMain(netdataDashboard.os, data.charts, duration);
1902
-
1903
- // sort the menus
1904
- var main = sortObjectByPriority(menus);
1905
- var i = 0, len = main.length;
1906
- while(i < len) {
1907
- var menu = main[i++];
1908
-
1909
- // generate an entry at the main menu
1910
-
1911
- var menuid = NETDATA.name2id('menu_' + menu);
1912
- sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].icon + ' ' + menus[menu].title + '</a><ul class="nav">';
1913
- html += '<div role="section" class="dashboard-section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].icon + ' ' + menus[menu].title + '</h1></div><div role="section" class="dashboard-subsection">';
1914
-
1915
- if(menus[menu].info !== null)
1916
- html += menus[menu].info;
1917
-
1918
- // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title);
1919
-
1920
- var shtml = '';
1921
- var mhead = '<div class="netdata-chart-row">' + mainhead;
1922
- mainhead = '';
1923
-
1924
- // sort the submenus of this menu
1925
- var sub = sortObjectByPriority(menus[menu].submenus);
1926
- var si = 0, slen = sub.length;
1927
- while(si < slen) {
1928
- var submenu = sub[si++];
1929
-
1930
- // generate an entry at the submenu
1931
- var submenuid = NETDATA.name2id('menu_' + menu + '_submenu_' + submenu);
1932
- sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>';
1933
- shtml += '<div role="section" class="dashboard-section-container" id="' + submenuid + '"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>';
1934
-
1935
- if(menus[menu].submenus[submenu].info !== null)
1936
- shtml += '<div class="dashboard-submenu-info netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>';
1937
-
1938
- var head = '<div class="netdata-chart-row">';
1939
- var chtml = '';
1940
-
1941
- // console.log(' \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title);
1942
-
1943
- // sort the charts in this submenu of this menu
1944
- menus[menu].submenus[submenu].charts.sort(prioritySort);
1945
- var ci = 0, clen = menus[menu].submenus[submenu].charts.length;
1946
- while(ci < clen) {
1947
- var chart = menus[menu].submenus[submenu].charts[ci++];
1948
-
1949
- // generate the submenu heading charts
1950
- mhead += generateHeadCharts('mainheads', chart, duration);
1951
- head += generateHeadCharts('heads', chart, duration);
1952
-
1953
- function chartCommonMin(family, context, units) {
1954
- var x = netdataDashboard.anyAttribute(netdataDashboard.context, 'commonMin', context, undefined);
1955
- if(typeof x !== 'undefined')
1956
- return ' data-common-min="' + family + '/' + context + '/' + units + '"';
1957
- else
1958
- return '';
1959
- }
1960
-
1961
- function chartCommonMax(family, context, units) {
1962
- var x = netdataDashboard.anyAttribute(netdataDashboard.context, 'commonMax', context, undefined);
1963
- if(typeof x !== 'undefined')
1964
- return ' data-common-max="' + family + '/' + context + '/' + units + '"';
1965
- else
1966
- return '';
1967
- }
1968
-
1969
- // generate the chart
1970
- if(urlOptions.mode === 'print')
1971
- chtml += '<div role="row" class="dashboard-print-row">';
1972
-
1973
- chtml += '<div class="netdata-chartblock-container" style="width: ' + pcent_width.toString() + '%;">' + netdataDashboard.contextInfo(chart.context) + '<div class="netdata-container" id="chart_' + NETDATA.name2id(chart.id) + '" data-netdata="' + chart.id + '"'
1974
- + ' data-width="100%"'
1975
- + ' data-height="' + netdataDashboard.contextHeight(chart.context, options.chartsHeight).toString() + 'px"'
1976
- + ' data-dygraph-valuerange="' + netdataDashboard.contextValueRange(chart.context) + '"'
1977
- + ' data-before="0"'
1978
- + ' data-after="-' + duration.toString() + '"'
1979
- + ' data-id="' + NETDATA.name2id(options.hostname + '/' + chart.id) + '"'
1980
- + ' data-colors="' + netdataDashboard.anyAttribute(netdataDashboard.context, 'colors', chart.context, '') + '"'
1981
- + ' data-decimal-digits="' + netdataDashboard.contextDecimalDigits(chart.context, -1) + '"'
1982
- + chartCommonMin(chart.family, chart.context, chart.units)
1983
- + chartCommonMax(chart.family, chart.context, chart.units)
1984
- + ' role="application"></div></div>';
1985
-
1986
- if(urlOptions.mode === 'print')
1987
- chtml += '</div>';
1988
-
1989
- // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height);
1990
- }
1991
-
1992
- head += '</div>';
1993
- shtml += head + chtml + '</div>';
1994
- }
1995
-
1996
- mhead += '</div>';
1997
- sidebar += '</ul></li>';
1998
- html += mhead + shtml + '</div></div><hr role="separator"/>';
1999
- }
2000
-
2001
- sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/netdata/netdata/wiki/Add-more-charts-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more charts</a></li>';
2002
- sidebar += '<li class=""><a href="https://github.com/netdata/netdata/wiki/Add-more-alarms-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more alarms</a></li>';
2003
- sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every === 1)?'second':data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, { space: ' ' }) + ' of real-time history.<br/> <br/><b>netdata</b><br/>v' + data.version.toString() +'</small></li>';
2004
- sidebar += '</ul>';
2005
- div.innerHTML = html;
2006
- document.getElementById('sidebar').innerHTML = sidebar;
2007
-
2008
- if(urlOptions.highlight === true)
2009
- NETDATA.globalChartUnderlay.init(null
2010
- , urlOptions.highlight_after
2011
- , urlOptions.highlight_before
2012
- , (urlOptions.after > 0) ? urlOptions.after : null
2013
- , (urlOptions.before > 0) ? urlOptions.before : null
2014
- );
2015
- else
2016
- NETDATA.globalChartUnderlay.clear();
2017
-
2018
- if(urlOptions.mode === 'print')
2019
- printPage();
2020
- else
2021
- finalizePage();
2022
- }
2023
-
2024
- function renderChartsAndMenu(data) {
2025
- options.menus = {};
2026
- options.submenu_names = {};
2027
-
2028
- var menus = options.menus;
2029
- var charts = data.charts;
2030
- var m, menu_key;
2031
-
2032
- for(var c in charts) {
2033
- if(!charts.hasOwnProperty(c)) continue;
2034
-
2035
- var chart = charts[c];
2036
- enrichChartData(chart);
2037
- m = chart.menu;
2038
-
2039
- // create the menu
2040
- if(typeof menus[m] === 'undefined') {
2041
- menus[m] = {
2042
- menu_pattern: chart.menu_pattern,
2043
- priority: chart.priority,
2044
- submenus: {},
2045
- title: netdataDashboard.menuTitle(chart),
2046
- icon: netdataDashboard.menuIcon(chart),
2047
- info: netdataDashboard.menuInfo(chart),
2048
- height: netdataDashboard.menuHeight(chart) * options.chartsHeight
2049
- };
2050
- }
2051
- else {
2052
- if(typeof(menus[m].menu_pattern) === 'undefined')
2053
- menus[m].menu_pattern = chart.menu_pattern;
2054
-
2055
- if(chart.priority < menus[m].priority)
2056
- menus[m].priority = chart.priority;
2057
- }
2058
-
2059
- menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m;
2060
-
2061
- // create the submenu
2062
- if(typeof menus[m].submenus[chart.submenu] === 'undefined') {
2063
- menus[m].submenus[chart.submenu] = {
2064
- priority: chart.priority,
2065
- charts: [],
2066
- title: null,
2067
- info: netdataDashboard.submenuInfo(menu_key, chart.submenu),
2068
- height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height)
2069
- };
2070
- }
2071
- else {
2072
- if (chart.priority < menus[m].submenus[chart.submenu].priority)
2073
- menus[m].submenus[chart.submenu].priority = chart.priority;
2074
- }
2075
-
2076
- // index the chart in the menu/submenu
2077
- menus[m].submenus[chart.submenu].charts.push(chart);
2078
- }
2079
-
2080
- // propagate the descriptive subname given to QoS
2081
- // to all the other submenus with the same name
2082
- for(m in menus) {
2083
- if(!menus.hasOwnProperty(m)) continue;
2084
-
2085
- for(var s in menus[m].submenus) {
2086
- if(!menus[m].submenus.hasOwnProperty(s)) continue;
2087
-
2088
- // set the family using a name
2089
- if(typeof options.submenu_names[s] !== 'undefined') {
2090
- menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')';
2091
- }
2092
- else {
2093
- menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m;
2094
- menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s);
2095
- }
2096
- }
2097
- }
2098
-
2099
- renderPage(menus, data);
2100
- }
2101
-
2102
- // ----------------------------------------------------------------------------
2103
-
2104
- function loadJs(url, callback) {
2105
- $.ajax({
2106
- url: url,
2107
- cache: true,
2108
- dataType: "script",
2109
- xhrFields: { withCredentials: true } // required for the cookie
2110
- })
2111
- .fail(function() {
2112
- alert('Cannot load required JS library: ' + url);
2113
- })
2114
- .always(function() {
2115
- if(typeof callback === 'function')
2116
- callback();
2117
- })
2118
- }
2119
-
2120
- var clipboardLoaded = false;
2121
- function loadClipboard(callback) {
2122
- if(clipboardLoaded === false) {
2123
- clipboardLoaded = true;
2124
- loadJs('lib/clipboard-polyfill-be05dad.js', callback);
2125
- }
2126
- else callback();
2127
- }
2128
-
2129
- var bootstrapTableLoaded = false;
2130
- function loadBootstrapTable(callback) {
2131
- if(bootstrapTableLoaded === false) {
2132
- bootstrapTableLoaded = true;
2133
- loadJs('lib/bootstrap-table-1.11.0.min.js', function() {
2134
- loadJs('lib/bootstrap-table-export-1.11.0.min.js', function() {
2135
- loadJs('lib/tableExport-1.6.0.min.js', callback);
2136
- })
2137
- });
2138
- }
2139
- else callback();
2140
- }
2141
-
2142
- var bootstrapSliderLoaded = false;
2143
- function loadBootstrapSlider(callback) {
2144
- if(bootstrapSliderLoaded === false) {
2145
- bootstrapSliderLoaded = true;
2146
- loadJs('lib/bootstrap-slider-10.0.0.min.js', function() {
2147
- NETDATA._loadCSS('css/bootstrap-slider-10.0.0.min.css');
2148
- callback();
2149
- });
2150
- }
2151
- else callback();
2152
- }
2153
-
2154
- var lzStringLoaded = false;
2155
- function loadLzString(callback) {
2156
- if(lzStringLoaded === false) {
2157
- lzStringLoaded = true;
2158
- loadJs('lib/lz-string-1.4.4.min.js', callback);
2159
- }
2160
- else callback();
2161
- }
2162
-
2163
- var pakoLoaded = false;
2164
- function loadPako(callback) {
2165
- if(pakoLoaded === false) {
2166
- pakoLoaded = true;
2167
- loadJs('lib/pako-1.0.6.min.js', callback);
2168
- }
2169
- else callback();
2170
- }
2171
-
2172
- // ----------------------------------------------------------------------------
2173
-
2174
- function clipboardCopy(text) {
2175
- clipboard.writeText(text);
2176
- }
2177
- function clipboardCopyBadgeEmbed(url) {
2178
- clipboard.writeText('<embed src="' + url + '" type="image/svg+xml" height="20"/>');
2179
- }
2180
-
2181
-
2182
- // ----------------------------------------------------------------------------
2183
-
2184
- function alarmsUpdateModal() {
2185
- var active = '<h3>Raised Alarms</h3><table class="table">';
2186
- var all = '<h3>All Running Alarms</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">';
2187
- var footer = '<hr/><a href="https://github.com/netdata/netdata/tree/master/web/api/badges#netdata-badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b> red </b></span> is critical, <span style="color:#fe7d37"><b> orange </b></span> is warning, <span style="color: #4c1"><b> bright green </b></span> is ok, <span style="color: #9f9f9f"><b> light grey </b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b> black </b></span> is not initialized. You can copy and paste their URLs to embed them in any web page.<br/>netdata can send notifications for these alarms. Check <a href="https://github.com/netdata/netdata/blob/master/health/notifications/health_alarm_notify.conf">this configuration file</a> for more information.';
2188
-
2189
- loadClipboard(function() {});
2190
-
2191
- NETDATA.alarms.get('all', function(data) {
2192
- options.alarm_families = [];
2193
-
2194
- alarmsCallback(data);
2195
-
2196
- if(data === null) {
2197
- document.getElementById('alarms_active').innerHTML =
2198
- document.getElementById('alarms_all').innerHTML =
2199
- document.getElementById('alarms_log').innerHTML =
2200
- 'failed to load alarm data!';
2201
- return;
2202
- }
2203
-
2204
- function alarmid4human(id) {
2205
- if(id === 0)
2206
- return '-';
2207
-
2208
- return id.toString();
2209
- }
2210
-
2211
- function timestamp4human(timestamp, space) {
2212
- if(timestamp === 0)
2213
- return '-';
2214
-
2215
- if(typeof space === 'undefined')
2216
- space = ' ';
2217
-
2218
- var t = new Date(timestamp * 1000);
2219
- var now = new Date();
2220
-
2221
- if(t.toDateString() === now.toDateString())
2222
- return t.toLocaleTimeString();
2223
-
2224
- return t.toLocaleDateString() + space + t.toLocaleTimeString();
2225
- }
2226
-
2227
- function alarm_lookup_explain(alarm, chart) {
2228
- var dimensions = ' of all values ';
2229
-
2230
- if(chart.dimensions.length > 1)
2231
- dimensions = ' of the sum of all dimensions ';
2232
-
2233
- if(typeof alarm.lookup_dimensions !== 'undefined') {
2234
- var d = alarm.lookup_dimensions.replace(/|/g, ',');
2235
- var x = d.split(',');
2236
- if(x.length > 1)
2237
- dimensions = 'of the sum of dimensions <code>' + alarm.lookup_dimensions + '</code> ';
2238
- else
2239
- dimensions = 'of all values of dimension <code>' + alarm.lookup_dimensions + '</code> ';
2240
- }
2241
-
2242
- return '<code>' + alarm.lookup_method + '</code> '
2243
- + dimensions + ', of chart <code>' + alarm.chart + '</code>'
2244
- + ', starting <code>' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, { space: ' ' }) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, { space: ' ' }) + '</code>'
2245
- + ((alarm.lookup_options)?(', with options <code>' + alarm.lookup_options.replace(/ /g, ', ') + '</code>'):'')
2246
- + '.';
2247
- }
2248
-
2249
- function alarm_to_html(alarm, full) {
2250
- var chart = options.data.charts[alarm.chart];
2251
- if(typeof(chart) === 'undefined') {
2252
- chart = options.data.charts_by_name[alarm.chart];
2253
- if (typeof(chart) === 'undefined') {
2254
- // this means the charts loaded are incomplete
2255
- // probably netdata was restarted and more alarms
2256
- // are now available.
2257
- console.log('Cannot find chart ' + alarm.chart + ', you probably need to refresh the page.');
2258
- return '';
2259
- }
2260
- }
2261
-
2262
- var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined');
2263
- var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto';
2264
-
2265
- var action_buttons = '<br/> <br/>role: <b>' + alarm.recipient + '</b><br/> <br/>'
2266
- + '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
2267
- + '<div class="action-button ripple" title="click to copy to the clipboard the URL of this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopy(\'' + badge_url + '\'); return false;"><i class="far fa-copy"></i></div>'
2268
- + '<div class="action-button ripple" title="click to copy to the clipboard an auto-refreshing <code>embed</code> html element for this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopyBadgeEmbed(\'' + badge_url + '\'); return false;"><i class="fas fa-copy"></i></div>';
2269
-
2270
- var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/> <br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/> <br/><span style="font-size: 18px">' + alarm.info + '</span>' + action_buttons + '</td>'
2271
- + '<td><table class="table">'
2272
- + ((typeof alarm.warn !== 'undefined')?('<tr><td width="10%" style="text-align:right">warning when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>'):'')
2273
- + ((typeof alarm.crit !== 'undefined')?('<tr><td width="10%" style="text-align:right">critical when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>'):'');
2274
-
2275
- if(full === true) {
2276
- var units = chart.units;
2277
- if(units === '%') units = '%';
2278
-
2279
- html += ((typeof alarm.lookup_after !== 'undefined')?('<tr><td width="10%" style="text-align:right">db lookup</td><td>' + alarm_lookup_explain(alarm, chart) + '</td></tr>'):'')
2280
- + ((typeof alarm.calc !== 'undefined')?('<tr><td width="10%" style="text-align:right">calculation</td><td><span style="font-family: monospace;">' + alarm.calc + '</span></td></tr>'):'')
2281
- + ((chart.green !== null)?('<tr><td width="10%" style="text-align:right">green threshold</td><td><code>' + chart.green + ' ' + units + '</code></td></tr>'):'')
2282
- + ((chart.red !== null)?('<tr><td width="10%" style="text-align:right">red threshold</td><td><code>' + chart.red + ' ' + units + '</code></td></tr>'):'');
2283
- }
2284
-
2285
- var delay = '';
2286
- if((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) {
2287
- if(alarm.delay_up_duration === alarm.delay_down_duration) {
2288
- delay += '<small><br/>hysteresis ' + NETDATA.seconds4human(alarm.delay_up_duration, { space: ' ', negative_suffix: '' });
2289
- }
2290
- else {
2291
- delay = '<small><br/>hysteresis ';
2292
- if(alarm.delay_up_duration > 0) {
2293
- delay += 'on escalation <code>' + NETDATA.seconds4human(alarm.delay_up_duration, { space: ' ', negative_suffix: '' }) + '</code>, ';
2294
- }
2295
- if(alarm.delay_down_duration > 0) {
2296
- delay += 'on recovery <code>' + NETDATA.seconds4human(alarm.delay_down_duration, { space: ' ', negative_suffix: '' }) + '</code>, ';
2297
- }
2298
- }
2299
- if(alarm.delay_multiplier !== 1.0) {
2300
- delay += 'multiplied by <code>' + alarm.delay_multiplier.toString() + '</code>';
2301
- delay += ', up to <code>' + NETDATA.seconds4human(alarm.delay_max_duration, { space: ' ', negative_suffix: '' }) + '</code>';
2302
- }
2303
- delay += '</small>';
2304
- }
2305
-
2306
- html += '<tr><td width="10%" style="text-align:right">check every</td><td>' + NETDATA.seconds4human(alarm.update_every, { space: ' ', negative_suffix: '' }) + '</td></tr>'
2307
- + ((has_alarm === true)?('<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>'):'')
2308
- + '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>'
2309
- + '</table></td></tr>';
2310
-
2311
- return html;
2312
- }
2313
-
2314
- function alarm_family_show(id) {
2315
- var html = '<table class="table">';
2316
- var family = options.alarm_families[id];
2317
- var len = family.arr.length;
2318
- while(len--) {
2319
- var alarm = family.arr[len];
2320
- html += alarm_to_html(alarm, true);
2321
- }
2322
- html += '</table>';
2323
-
2324
- $('#alarm_all_' + id.toString()).html(html);
2325
- enableTooltipsAndPopovers();
2326
- }
2327
-
2328
- // find the proper family of each alarm
2329
- var x, family, alarm;
2330
- var count_active = 0;
2331
- var count_all = 0;
2332
- var families = {};
2333
- var families_sort = [];
2334
- for(x in data.alarms) {
2335
- if(!data.alarms.hasOwnProperty(x)) continue;
2336
-
2337
- alarm = data.alarms[x];
2338
- family = alarm.family;
2339
-
2340
- // find the chart
2341
- var chart = options.data.charts[alarm.chart];
2342
- if(typeof chart === 'undefined')
2343
- chart = options.data.charts_by_name[alarm.chart];
2344
-
2345
- // not found - this should never happen!
2346
- if(typeof chart === 'undefined') {
2347
- console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.');
2348
- chart = { priority: 9999999 };
2349
- }
2350
- else if(typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined')
2351
- // the family based on the chart
2352
- family = chart.menu + ' - ' + chart.submenu;
2353
-
2354
- if(typeof families[family] === 'undefined') {
2355
- families[family] = {
2356
- name: family,
2357
- arr: [],
2358
- priority: chart.priority
2359
- };
2360
-
2361
- families_sort.push(families[family]);
2362
- }
2363
-
2364
- if(chart.priority < families[family].priority)
2365
- families[family].priority = chart.priority;
2366
-
2367
- families[family].arr.unshift(alarm);
2368
- }
2369
-
2370
- // sort the families, like the dashboard menu does
2371
- var families_sorted = families_sort.sort(function (a, b) {
2372
- if (a.priority < b.priority) return -1;
2373
- if (a.priority > b.priority) return 1;
2374
- return naturalSortCompare(a.name, b.name);
2375
- });
2376
-
2377
- var i = 0;
2378
- var fc = 0;
2379
- var len = families_sorted.length;
2380
- while(len--) {
2381
- family = families_sorted[i++].name;
2382
- var active_family_added = false;
2383
- var expanded = 'true';
2384
- var collapsed = '';
2385
- var cin = 'in';
2386
-
2387
- if(fc !== 0) {
2388
- all += "</table></div></div></div>";
2389
- expanded = 'false';
2390
- collapsed = 'class="collapsed"';
2391
- cin = '';
2392
- }
2393
-
2394
- all += '<div class="panel panel-default"><div class="panel-heading" role="tab" id="alarm_all_heading_' + fc.toString() + '"><h4 class="panel-title"><a ' + collapsed + ' role="button" data-toggle="collapse" data-parent="#alarms_all_accordion" href="#alarm_all_' + fc.toString() + '" aria-expanded="' + expanded + '" aria-controls="alarm_all_' + fc.toString() + '">' + family.toString() + '</a></h4></div><div id="alarm_all_' + fc.toString() + '" class="panel-collapse collapse ' + cin + '" role="tabpanel" aria-labelledby="alarm_all_heading_' + fc.toString() + '" data-alarm-id="' + fc.toString() + '"><div class="panel-body" id="alarm_all_body_' + fc.toString() + '">';
2395
-
2396
- options.alarm_families[fc] = families[family];
2397
-
2398
- fc++;
2399
-
2400
- var arr = families[family].arr;
2401
- var c = arr.length;
2402
- while(c--) {
2403
- alarm = arr[c];
2404
- if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') {
2405
- if(!active_family_added) {
2406
- active_family_added = true;
2407
- active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>';
2408
- }
2409
- count_active++;
2410
- active += alarm_to_html(alarm, true);
2411
- }
2412
-
2413
- count_all++;
2414
- }
2415
- }
2416
- active += "</table>";
2417
- if(families_sorted.length > 0) all += "</div></div></div>";
2418
- all += "</div>";
2419
-
2420
- if(!count_active)
2421
- active += '<div style="width:100%; height: 100px; text-align: center;"><span style="font-size: 50px;"><i class="fas fa-thumbs-up"></i></span><br/>Everything is normal. No raised alarms.</div>';
2422
- else
2423
- active += footer;
2424
-
2425
- if(!count_all)
2426
- all += "<h4>No alarms are running in this system.</h4>";
2427
- else
2428
- all += footer;
2429
-
2430
- document.getElementById('alarms_active').innerHTML = active;
2431
- document.getElementById('alarms_all').innerHTML = all;
2432
- enableTooltipsAndPopovers();
2433
-
2434
- if(families_sorted.length > 0) alarm_family_show(0);
2435
-
2436
- // register bootstrap events
2437
- var $accordion = $('#alarms_all_accordion');
2438
- $accordion.on('show.bs.collapse', function (d) {
2439
- var target = $(d.target);
2440
- var id = $(target).data('alarm-id');
2441
- alarm_family_show(id);
2442
- });
2443
- $accordion.on('hidden.bs.collapse', function (d) {
2444
- var target = $(d.target);
2445
- var id = $(target).data('alarm-id');
2446
- $('#alarm_all_' + id.toString()).html('');
2447
- });
2448
-
2449
- document.getElementById('alarms_log').innerHTML = '<h3>Alarm Log</h3><table id="alarms_log_table"></table>';
2450
-
2451
- loadBootstrapTable(function () {
2452
- $('#alarms_log_table').bootstrapTable({
2453
- url: NETDATA.alarms.server + '/api/v1/alarm_log?all',
2454
- cache: false,
2455
- pagination: true,
2456
- pageSize: 10,
2457
- showPaginationSwitch: false,
2458
- search: true,
2459
- searchTimeOut: 300,
2460
- searchAlign: 'left',
2461
- showColumns: true,
2462
- showExport: true,
2463
- exportDataType: 'basic',
2464
- exportOptions: {
2465
- fileName: 'netdata_alarm_log'
2466
- },
2467
- rowStyle: function(row, index) {
2468
- void(index);
2469
-
2470
- switch(row.status) {
2471
- case 'CRITICAL' : return { classes: 'danger' }; break;
2472
- case 'WARNING' : return { classes: 'warning' }; break;
2473
- case 'UNDEFINED': return { classes: 'info' }; break;
2474
- case 'CLEAR' : return { classes: 'success' }; break;
2475
- }
2476
- return {};
2477
- },
2478
- showFooter: false,
2479
- showHeader: true,
2480
- showRefresh: true,
2481
- showToggle: false,
2482
- sortable: true,
2483
- silentSort: false,
2484
- columns: [
2485
- {
2486
- field: 'when',
2487
- title: 'Event Date',
2488
- valign: 'middle',
2489
- titleTooltip: 'The date and time the even took place',
2490
- formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); },
2491
- align: 'center',
2492
- switchable: false,
2493
- sortable: true
2494
- },
2495
- {
2496
- field: 'hostname',
2497
- title: 'Host',
2498
- valign: 'middle',
2499
- titleTooltip: 'The host that generated this event',
2500
- align: 'center',
2501
- visible: false,
2502
- sortable: true
2503
- },
2504
- {
2505
- field: 'unique_id',
2506
- title: 'Unique ID',
2507
- titleTooltip: 'The host unique ID for this event',
2508
- formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); },
2509
- align: 'center',
2510
- valign: 'middle',
2511
- visible: false,
2512
- sortable: true
2513
- },
2514
- {
2515
- field: 'alarm_id',
2516
- title: 'Alarm ID',
2517
- titleTooltip: 'The ID of the alarm that generated this event',
2518
- formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); },
2519
- align: 'center',
2520
- valign: 'middle',
2521
- visible: false,
2522
- sortable: true
2523
- },
2524
- {
2525
- field: 'alarm_event_id',
2526
- title: 'Alarm Event ID',
2527
- titleTooltip: 'The incremental ID of this event for the given alarm',
2528
- formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); },
2529
- align: 'center',
2530
- valign: 'middle',
2531
- visible: false,
2532
- sortable: true
2533
- },
2534
- {
2535
- field: 'chart',
2536
- title: 'Chart',
2537
- titleTooltip: 'The chart the alarm is attached to',
2538
- align: 'center',
2539
- valign: 'middle',
2540
- switchable: false,
2541
- sortable: true
2542
- },
2543
- {
2544
- field: 'family',
2545
- title: 'Family',
2546
- titleTooltip: 'The family of the chart the alarm is attached to',
2547
- align: 'center',
2548
- valign: 'middle',
2549
- visible: false,
2550
- sortable: true
2551
- },
2552
- {
2553
- field: 'name',
2554
- title: 'Alarm',
2555
- titleTooltip: 'The alarm name that generated this event',
2556
- formatter: function(value, row, index) {
2557
- void(row);
2558
- void(index);
2559
- return value.toString().replace(/_/g, ' ');
2560
- },
2561
- align: 'center',
2562
- valign: 'middle',
2563
- switchable: false,
2564
- sortable: true
2565
- },
2566
- {
2567
- field: 'value_string',
2568
- title: 'Friendly Value',
2569
- titleTooltip: 'The value of the alarm, that triggered this event',
2570
- align: 'right',
2571
- valign: 'middle',
2572
- sortable: true
2573
- },
2574
- {
2575
- field: 'old_value_string',
2576
- title: 'Friendly Old Value',
2577
- titleTooltip: 'The value of the alarm, just before this event',
2578
- align: 'right',
2579
- valign: 'middle',
2580
- visible: false,
2581
- sortable: true
2582
- },
2583
- {
2584
- field: 'old_value',
2585
- title: 'Old Value',
2586
- titleTooltip: 'The value of the alarm, just before this event',
2587
- formatter: function(value, row, index) {
2588
- void(row);
2589
- void(index);
2590
- return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString();
2591
- },
2592
- align: 'center',
2593
- valign: 'middle',
2594
- visible: false,
2595
- sortable: true
2596
- },
2597
- {
2598
- field: 'value',
2599
- title: 'Value',
2600
- titleTooltip: 'The value of the alarm, that triggered this event',
2601
- formatter: function(value, row, index) {
2602
- void(row);
2603
- void(index);
2604
- return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString();
2605
- },
2606
- align: 'right',
2607
- valign: 'middle',
2608
- visible: false,
2609
- sortable: true
2610
- },
2611
- {
2612
- field: 'units',
2613
- title: 'Units',
2614
- titleTooltip: 'The units of the value of the alarm',
2615
- align: 'left',
2616
- valign: 'middle',
2617
- visible: false,
2618
- sortable: true
2619
- },
2620
- {
2621
- field: 'old_status',
2622
- title: 'Old Status',
2623
- titleTooltip: 'The status of the alarm, just before this event',
2624
- align: 'center',
2625
- valign: 'middle',
2626
- visible: false,
2627
- sortable: true
2628
- },
2629
- {
2630
- field: 'status',
2631
- title: 'Status',
2632
- titleTooltip: 'The status of the alarm, that was set due to this event',
2633
- align: 'center',
2634
- valign: 'middle',
2635
- switchable: false,
2636
- sortable: true
2637
- },
2638
- {
2639
- field: 'duration',
2640
- title: 'Last Duration',
2641
- titleTooltip: 'The duration the alarm was at its previous state, just before this event',
2642
- formatter: function(value, row, index) {
2643
- void(row);
2644
- void(index);
2645
- return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
2646
- },
2647
- align: 'center',
2648
- valign: 'middle',
2649
- visible: false,
2650
- sortable: true
2651
- },
2652
- {
2653
- field: 'non_clear_duration',
2654
- title: 'Raised Duration',
2655
- titleTooltip: 'The duration the alarm was raised, just before this event',
2656
- formatter: function(value, row, index) {
2657
- void(row);
2658
- void(index);
2659
- return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
2660
- },
2661
- align: 'center',
2662
- valign: 'middle',
2663
- visible: false,
2664
- sortable: true
2665
- },
2666
- {
2667
- field: 'recipient',
2668
- title: 'Recipient',
2669
- titleTooltip: 'The recipient of this event',
2670
- align: 'center',
2671
- valign: 'middle',
2672
- visible: false,
2673
- sortable: true
2674
- },
2675
- {
2676
- field: 'processed',
2677
- title: 'Processed Status',
2678
- titleTooltip: 'True when this event is processed',
2679
- formatter: function(value, row, index) {
2680
- void(row);
2681
- void(index);
2682
-
2683
- if(value === true)
2684
- return 'DONE';
2685
- else
2686
- return 'PENDING';
2687
- },
2688
- align: 'center',
2689
- valign: 'middle',
2690
- visible: false,
2691
- sortable: true
2692
- },
2693
- {
2694
- field: 'updated',
2695
- title: 'Updated Status',
2696
- titleTooltip: 'True when this event has been updated by another event',
2697
- formatter: function(value, row, index) {
2698
- void(row);
2699
- void(index);
2700
-
2701
- if(value === true)
2702
- return 'UPDATED';
2703
- else
2704
- return 'CURRENT';
2705
- },
2706
- align: 'center',
2707
- valign: 'middle',
2708
- visible: false,
2709
- sortable: true
2710
- },
2711
- {
2712
- field: 'updated_by_id',
2713
- title: 'Updated By ID',
2714
- titleTooltip: 'The unique ID of the event that obsoleted this one',
2715
- formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); },
2716
- align: 'center',
2717
- valign: 'middle',
2718
- visible: false,
2719
- sortable: true
2720
- },
2721
- {
2722
- field: 'updates_id',
2723
- title: 'Updates ID',
2724
- titleTooltip: 'The unique ID of the event obsoleted because of this event',
2725
- formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); },
2726
- align: 'center',
2727
- valign: 'middle',
2728
- visible: false,
2729
- sortable: true
2730
- },
2731
- {
2732
- field: 'exec',
2733
- title: 'Script',
2734
- titleTooltip: 'The script to handle the event notification',
2735
- align: 'center',
2736
- valign: 'middle',
2737
- visible: false,
2738
- sortable: true
2739
- },
2740
- {
2741
- field: 'exec_run',
2742
- title: 'Script Run At',
2743
- titleTooltip: 'The date and time the script has been ran',
2744
- formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); },
2745
- align: 'center',
2746
- valign: 'middle',
2747
- visible: false,
2748
- sortable: true
2749
- },
2750
- {
2751
- field: 'exec_code',
2752
- title: 'Script Return Value',
2753
- titleTooltip: 'The return code of the script',
2754
- formatter: function(value, row, index) {
2755
- void(row);
2756
- void(index);
2757
-
2758
- if(value === 0)
2759
- return 'OK (returned 0)';
2760
- else
2761
- return 'FAILED (with code ' + value.toString() + ')';
2762
- },
2763
- align: 'center',
2764
- valign: 'middle',
2765
- visible: false,
2766
- sortable: true
2767
- },
2768
- {
2769
- field: 'delay',
2770
- title: 'Script Delay',
2771
- titleTooltip: 'The hysteresis of the notification',
2772
- formatter: function(value, row, index) {
2773
- void(row);
2774
- void(index);
2775
-
2776
- return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
2777
- },
2778
- align: 'center',
2779
- valign: 'middle',
2780
- visible: false,
2781
- sortable: true
2782
- },
2783
- {
2784
- field: 'delay_up_to_timestamp',
2785
- title: 'Script Delay Run At',
2786
- titleTooltip: 'The date and time the script should be run, after hysteresis',
2787
- formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); },
2788
- align: 'center',
2789
- valign: 'middle',
2790
- visible: false,
2791
- sortable: true
2792
- },
2793
- {
2794
- field: 'info',
2795
- title: 'Description',
2796
- titleTooltip: 'A short description of the alarm',
2797
- align: 'center',
2798
- valign: 'middle',
2799
- visible: false,
2800
- sortable: true
2801
- },
2802
- {
2803
- field: 'source',
2804
- title: 'Alarm Source',
2805
- titleTooltip: 'The source of configuration of the alarm',
2806
- align: 'center',
2807
- valign: 'middle',
2808
- visible: false,
2809
- sortable: true
2810
- }
2811
- ]
2812
- });
2813
- // console.log($('#alarms_log_table').bootstrapTable('getOptions'));
2814
- });
2815
- });
2816
- }
2817
-
2818
- function alarmsCallback(data) {
2819
- var count = 0, x;
2820
- for(x in data.alarms) {
2821
- if(!data.alarms.hasOwnProperty(x)) continue;
2822
-
2823
- var alarm = data.alarms[x];
2824
- if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL')
2825
- count++;
2826
- }
2827
-
2828
- if(count > 0)
2829
- document.getElementById('alarms_count_badge').innerHTML = count.toString();
2830
- else
2831
- document.getElementById('alarms_count_badge').innerHTML = '';
2832
- }
2833
-
2834
- function initializeDynamicDashboardWithData(data) {
2835
- if(data !== null) {
2836
- options.hostname = data.hostname;
2837
- options.data = data;
2838
- options.version = data.version;
2839
- netdataDashboard.os = data.os;
2840
-
2841
- if(typeof data.hosts !=='undefined')
2842
- options.hosts = data.hosts;
2843
-
2844
- // update the dashboard hostname
2845
- document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null)?' (snap)':'').toString();
2846
- document.getElementById('hostname').href = NETDATA.serverDefault;
2847
- document.getElementById('netdataVersion').innerHTML = options.version;
2848
-
2849
- if(netdataSnapshotData !== null) {
2850
- $('#alarmsButton').hide();
2851
- $('#updateButton').hide();
2852
- // $('#loadButton').hide();
2853
- $('#saveButton').hide();
2854
- $('#printButton').hide();
2855
- }
2856
-
2857
- // update the dashboard title
2858
- document.title = options.hostname + ' netdata dashboard';
2859
-
2860
- // close the splash screen
2861
- $("#loadOverlay").css("display","none");
2862
-
2863
- // create a chart_by_name index
2864
- data.charts_by_name = {};
2865
- var charts = data.charts;
2866
- var x;
2867
- for(x in charts) {
2868
- if(!charts.hasOwnProperty(x)) continue;
2869
-
2870
- var chart = charts[x];
2871
- data.charts_by_name[chart.name] = chart;
2872
- }
2873
-
2874
- // render all charts
2875
- renderChartsAndMenu(data);
2876
- }
2877
- }
2878
-
2879
- // an object to keep initilization configuration
2880
- // needed due to the async nature of the XSS modal
2881
- var initializeConfig = {
2882
- url: null,
2883
- custom_info: true,
2884
- };
2885
-
2886
- function loadCustomDashboardInfo(url, callback) {
2887
- loadJs(url, function () {
2888
- $.extend(true, netdataDashboard, customDashboard);
2889
- callback();
2890
- });
2891
- }
2892
-
2893
- function initializeChartsAndCustomInfo() {
2894
- NETDATA.alarms.callback = alarmsCallback;
2895
-
2896
- // download all the charts the server knows
2897
- NETDATA.chartRegistry.downloadAll(initializeConfig.url, function(data) {
2898
- if(data !== null) {
2899
- if (initializeConfig.custom_info === true && typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) {
2900
- //console.log('loading custom dashboard decorations from server ' + initializeConfig.url);
2901
- loadCustomDashboardInfo(NETDATA.serverDefault + data.custom_info, function () {
2902
- initializeDynamicDashboardWithData(data);
2903
- });
2904
- }
2905
- else {
2906
- //console.log('not loading custom dashboard decorations from server ' + initializeConfig.url);
2907
- initializeDynamicDashboardWithData(data);
2908
- }
2909
- }
2910
- });
2911
- }
2912
-
2913
- function xssModalDisableXss() {
2914
- //console.log('disabling xss checks');
2915
- NETDATA.xss.enabled = false;
2916
- NETDATA.xss.enabled_for_data = false;
2917
- initializeConfig.custom_info = true;
2918
- initializeChartsAndCustomInfo();
2919
- return false;
2920
- }
2921
-
2922
- function xssModalKeepXss() {
2923
- //console.log('keeping xss checks');
2924
- NETDATA.xss.enabled = true;
2925
- NETDATA.xss.enabled_for_data = true;
2926
- initializeConfig.custom_info = false;
2927
- initializeChartsAndCustomInfo();
2928
- return false;
2929
- }
2930
-
2931
- function initializeDynamicDashboard(netdata_url) {
2932
- if(typeof netdata_url === 'undefined' || netdata_url === null)
2933
- netdata_url = NETDATA.serverDefault;
2934
-
2935
- initializeConfig.url = netdata_url;
2936
-
2937
- // initialize clickable alarms
2938
- NETDATA.alarms.chart_div_offset = -50;
2939
- NETDATA.alarms.chart_div_id_prefix = 'chart_';
2940
- NETDATA.alarms.chart_div_animation_duration = 0;
2941
-
2942
- NETDATA.pause(function() {
2943
- if(typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true) {
2944
- //$("#loadOverlay").css("display","none");
2945
- document.getElementById('netdataXssModalServer').innerText = initializeConfig.url;
2946
- $('#xssModal').modal('show');
2947
- }
2948
- else {
2949
- initializeChartsAndCustomInfo();
2950
- }
2951
- });
2952
- }
2953
-
2954
- // ----------------------------------------------------------------------------
2955
-
2956
- function versionLog(msg) {
2957
- document.getElementById('versionCheckLog').innerHTML = msg;
2958
- }
2959
-
2960
- function getNetdataCommitIdFromVersion() {
2961
- var s = options.version.split('-');
2962
-
2963
- if(s.length !== 3) return null;
2964
- if(s[2][0] === 'g') {
2965
- var v = s[2].split('_')[0].substring(1, 8);
2966
- if(v.length === 7) {
2967
- versionLog('Installed git commit id of netdata is ' + v);
2968
- document.getElementById('netdataCommitId').innerHTML = v;
2969
- return v;
2970
- }
2971
- }
2972
- return null;
2973
- }
2974
-
2975
- function getNetdataCommitId(force, callback) {
2976
- versionLog('Downloading installed git commit id from netdata...');
2977
-
2978
- $.ajax({
2979
- url: 'version.txt',
2980
- async: true,
2981
- cache: false,
2982
- xhrFields: { withCredentials: true } // required for the cookie
2983
- })
2984
- .done(function(data) {
2985
- data = data.replace(/(\r\n|\n|\r| |\t)/gm,"");
2986
-
2987
- var c = getNetdataCommitIdFromVersion();
2988
- if(c !== null && data.length === 40 && data.substring(0, 7) !== c) {
2989
- versionLog('Installed files commit id and internal netdata git commit id do not match');
2990
- data = c;
2991
- }
2992
-
2993
- if(data.length >= 7) {
2994
- versionLog('Installed git commit id of netdata is ' + data);
2995
- document.getElementById('netdataCommitId').innerHTML = data.substring(0, 7);
2996
- callback(data);
2997
- }
2998
- })
2999
- .fail(function() {
3000
- versionLog('Failed to download installed git commit id from netdata!');
3001
-
3002
- if(force === true) {
3003
- var c = getNetdataCommitIdFromVersion();
3004
- if(c === null) versionLog('Cannot find the git commit id of netdata.');
3005
- callback(c);
3006
- }
3007
- else
3008
- callback(null);
3009
- });
3010
- }
3011
-
3012
- function getGithubLatestCommit(callback) {
3013
- versionLog('Downloading latest git commit id info from github...');
3014
-
3015
- $.ajax({
3016
- url: 'https://api.github.com/repos/netdata/netdata/commits',
3017
- async: true,
3018
- cache: false
3019
- })
3020
- .done(function(data) {
3021
- versionLog('Latest git commit id from github is ' + data[0].sha);
3022
- callback(data[0].sha);
3023
- })
3024
- .fail(function() {
3025
- versionLog('Failed to download installed git commit id from github!');
3026
- callback(null);
3027
- });
3028
- }
3029
-
3030
- function checkForUpdate(force, callback) {
3031
- getNetdataCommitId(force, function(sha1) {
3032
- if(sha1 === null) callback(null, null);
3033
-
3034
- getGithubLatestCommit(function(sha2) {
3035
- callback(sha1, sha2);
3036
- });
3037
- });
3038
-
3039
- return null;
3040
- }
3041
-
3042
- function notifyForUpdate(force) {
3043
- versionLog('<p>checking for updates...</p>');
3044
-
3045
- var now = Date.now();
3046
-
3047
- if(typeof force === 'undefined' || force !== true) {
3048
- var last = loadLocalStorage('last_update_check');
3049
-
3050
- if(typeof last === 'string')
3051
- last = parseInt(last);
3052
- else
3053
- last = 0;
3054
-
3055
- if(now - last < 3600000 * 8) {
3056
- // no need to check it - too soon
3057
- return;
3058
- }
3059
- }
3060
-
3061
- checkForUpdate(force, function(sha1, sha2) {
3062
- var save = false;
3063
-
3064
- if(sha1 === null) {
3065
- save = false;
3066
- versionLog('<p><big>Failed to get your netdata git commit id!</big></p><p>You can always get the latest netdata from <a href="https://github.com/netdata/netdata" target="_blank">its github page</a>.</p>');
3067
- }
3068
- else if(sha2 === null) {
3069
- save = false;
3070
- versionLog('<p><big>Failed to get the latest git commit id from github.</big></p><p>You can always get the latest netdata from <a href="https://github.com/netdata/netdata" target="_blank">its github page</a>.</p>');
3071
- }
3072
- else if(sha1 === sha2) {
3073
- save = true;
3074
- versionLog('<p><big>You already have the latest netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/netdata/netdata" target="_blank">give netdata a <b><i class="fas fa-star"></i></b> at its github page</a>.</p>');
3075
- }
3076
- else {
3077
- save = true;
3078
- var compare = 'https://github.com/netdata/netdata/compare/' + sha1.toString() + '...' + sha2.toString();
3079
-
3080
- versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest commit: <b><code>' + sha2.substring(0, 7).toString() + '</code></b></p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/netdata/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>');
3081
-
3082
- document.getElementById('update_badge').innerHTML = '!';
3083
- }
3084
-
3085
- if(save)
3086
- saveLocalStorage('last_update_check', now.toString());
3087
- });
3088
- }
3089
-
3090
- // ----------------------------------------------------------------------------
3091
- // printing dashboards
3092
-
3093
- function showPageFooter() {
3094
- document.getElementById('footer').style.display = 'block';
3095
- }
3096
-
3097
-
3098
- function printPreflight() {
3099
- var url = document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString() + urlOptions.genHash() + ';mode=print';
3100
- var width = 990;
3101
- var height = screen.height * 90 / 100;
3102
- //console.log(url);
3103
- //console.log(document.location);
3104
- window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes');
3105
- $('#printPreflightModal').modal('hide');
3106
- }
3107
-
3108
- function printPage() {
3109
- var print_is_rendering = true;
3110
-
3111
- $('#printModal').on('hide.bs.modal', function(e) {
3112
- if(print_is_rendering === true) {
3113
- e.preventDefault();
3114
- return false;
3115
- }
3116
-
3117
- return true;
3118
- });
3119
-
3120
- $('#printModal').on('show.bs.modal', function() {
3121
- var print_options = {
3122
- stop_updates_when_focus_is_lost: false,
3123
- update_only_visible: false,
3124
- sync_selection: false,
3125
- eliminate_zero_dimensions: false,
3126
- pan_and_zoom_data_padding: false,
3127
- show_help: false,
3128
- legend_toolbox: false,
3129
- resize_charts: false,
3130
- pixels_per_point: 1
3131
- };
3132
-
3133
- var x;
3134
- for(x in print_options) {
3135
- if (print_options.hasOwnProperty(x))
3136
- NETDATA.options.current[x] = print_options[x];
3137
- }
3138
-
3139
- NETDATA.parseDom();
3140
- showPageFooter();
3141
-
3142
- NETDATA.globalSelectionSync.stop();
3143
- NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
3144
- // NETDATA.onresize();
3145
-
3146
- var el = document.getElementById('printModalProgressBar');
3147
- var eltxt = document.getElementById('printModalProgressBarText');
3148
-
3149
- function update_chart(idx) {
3150
- var state = NETDATA.options.targets[--idx];
3151
-
3152
- var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length;
3153
- $(el).css('width', pcent+'%').attr('aria-valuenow', pcent);
3154
- eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id;
3155
-
3156
- setTimeout(function() {
3157
- state.updateChart(function () {
3158
- NETDATA.options.targets[idx].resizeForPrint();
3159
-
3160
- if (idx > 0) {
3161
- update_chart(idx);
3162
- }
3163
- else {
3164
- print_is_rendering = false;
3165
- $('#printModal').modal('hide');
3166
- window.print();
3167
- window.close();
3168
- }
3169
- })
3170
- }, 0);
3171
- }
3172
-
3173
- print_is_rendering = true;
3174
- update_chart(NETDATA.options.targets.length);
3175
- });
3176
-
3177
- $('#printModal').modal('show');
3178
- }
3179
-
3180
- // --------------------------------------------------------------------
3181
-
3182
- function jsonStringifyFn(obj) {
3183
- return JSON.stringify(obj, function (key, value) {
3184
- return (typeof value === 'function' ) ? value.toString() : value;
3185
- });
3186
- }
3187
-
3188
- function jsonParseFn(str) {
3189
- return JSON.parse(str, function (key, value) {
3190
- if (typeof value != 'string') return value;
3191
- return ( value.substring(0, 8) == 'function') ? eval('(' + value + ')') : value;
3192
- });
3193
- }
3194
-
3195
- // --------------------------------------------------------------------
3196
-
3197
- var snapshotOptions = {
3198
- bytes_per_chart: 2048,
3199
- compressionDefault: 'pako.deflate.base64',
3200
-
3201
- compressions: {
3202
- 'none': {
3203
- bytes_per_point_memory: 5.2,
3204
- bytes_per_point_disk: 5.6,
3205
-
3206
- compress: function (s) {
3207
- return s;
3208
- },
3209
-
3210
- compressed_length: function (s) {
3211
- return s.length;
3212
- },
3213
-
3214
- uncompress: function (s) {
3215
- return s;
3216
- }
3217
- },
3218
-
3219
- 'pako.deflate.base64': {
3220
- bytes_per_point_memory: 1.8,
3221
- bytes_per_point_disk: 1.9,
3222
-
3223
- compress: function (s) {
3224
- return btoa(pako.deflate(s, { to: 'string' }));
3225
- },
3226
-
3227
- compressed_length: function (s) {
3228
- return s.length;
3229
- },
3230
-
3231
- uncompress: function (s) {
3232
- return pako.inflate(atob(s), {to: 'string'});
3233
- }
3234
- },
3235
-
3236
- 'pako.deflate': {
3237
- bytes_per_point_memory: 1.4,
3238
- bytes_per_point_disk: 3.2,
3239
-
3240
- compress: function (s) {
3241
- return pako.deflate(s, { to: 'string' });
3242
- },
3243
-
3244
- compressed_length: function (s) {
3245
- return s.length;
3246
- },
3247
-
3248
- uncompress: function (s) {
3249
- return pako.inflate(s, {to: 'string'});
3250
- }
3251
- },
3252
-
3253
- 'lzstring.utf16': {
3254
- bytes_per_point_memory: 1.7,
3255
- bytes_per_point_disk: 2.6,
3256
-
3257
- compress: function (s) {
3258
- return LZString.compressToUTF16(s);
3259
- },
3260
-
3261
- compressed_length: function (s) {
3262
- return s.length * 2;
3263
- },
3264
-
3265
- uncompress: function (s) {
3266
- return LZString.decompressFromUTF16(s);
3267
- }
3268
- },
3269
-
3270
- 'lzstring.base64': {
3271
- bytes_per_point_memory: 2.1,
3272
- bytes_per_point_disk: 2.3,
3273
-
3274
- compress: function (s) {
3275
- return LZString.compressToBase64(s);
3276
- },
3277
-
3278
- compressed_length: function (s) {
3279
- return s.length;
3280
- },
3281
-
3282
- uncompress: function (s) {
3283
- return LZString.decompressFromBase64(s);
3284
- }
3285
- },
3286
-
3287
- 'lzstring.uri': {
3288
- bytes_per_point_memory: 2.1,
3289
- bytes_per_point_disk: 2.3,
3290
-
3291
- compress: function (s) {
3292
- return LZString.compressToEncodedURIComponent(s);
3293
- },
3294
-
3295
- compressed_length: function (s) {
3296
- return s.length;
3297
- },
3298
-
3299
- uncompress: function (s) {
3300
- return LZString.decompressFromEncodedURIComponent(s);
3301
- }
3302
- }
3303
- }
3304
- };
3305
-
3306
- // --------------------------------------------------------------------
3307
- // loading snapshots
3308
-
3309
- function loadSnapshotModalLog(priority, msg) {
3310
- document.getElementById('loadSnapshotStatus').className = "alert alert-" + priority;
3311
- document.getElementById('loadSnapshotStatus').innerHTML = msg;
3312
- }
3313
-
3314
- var tmpSnapshotData = null;
3315
- function loadSnapshot() {
3316
- $('#loadSnapshotImport').addClass('disabled');
3317
-
3318
- if(tmpSnapshotData === null) {
3319
- loadSnapshotPreflightEmpty();
3320
- loadSnapshotModalLog('danger', 'no data have been loaded');
3321
- return;
3322
- }
3323
-
3324
- loadPako(function() {
3325
- loadLzString(function () {
3326
- loadSnapshotModalLog('info', 'Please wait, activating snapshot...');
3327
- $('#loadSnapshotModal').modal('hide');
3328
-
3329
- netdataShowAlarms = false;
3330
- netdataRegistry = false;
3331
- netdataServer = tmpSnapshotData.server;
3332
- NETDATA.serverDefault = netdataServer;
3333
-
3334
- document.getElementById('charts_div').innerHTML = '';
3335
- document.getElementById('sidebar').innerHTML = '';
3336
- NETDATA.globalReset();
3337
-
3338
- if (typeof tmpSnapshotData.hash !== 'undefined')
3339
- urlOptions.hash = tmpSnapshotData.hash;
3340
- else
3341
- urlOptions.hash = '#';
3342
-
3343
- if (typeof tmpSnapshotData.info !== 'undefined') {
3344
- var info = jsonParseFn(tmpSnapshotData.info);
3345
- if (typeof info.menu !== 'undefined')
3346
- netdataDashboard.menu = info.menu;
3347
-
3348
- if (typeof info.submenu !== 'undefined')
3349
- netdataDashboard.submenu = info.submenu;
3350
-
3351
- if (typeof info.context !== 'undefined')
3352
- netdataDashboard.context = info.context;
3353
- }
3354
-
3355
- if (typeof tmpSnapshotData.compression !== 'string')
3356
- tmpSnapshotData.compression = 'none';
3357
-
3358
- if (typeof snapshotOptions.compressions[tmpSnapshotData.compression] === 'undefined') {
3359
- alert('unknown compression method: ' + tmpSnapshotData.compression);
3360
- tmpSnapshotData.compression = 'none';
3361
- }
3362
-
3363
- tmpSnapshotData.uncompress = snapshotOptions.compressions[tmpSnapshotData.compression].uncompress;
3364
- netdataSnapshotData = tmpSnapshotData;
3365
-
3366
- urlOptions.after = tmpSnapshotData.after_ms;
3367
- urlOptions.before = tmpSnapshotData.before_ms;
3368
-
3369
- if( typeof tmpSnapshotData.highlight_after_ms !== 'undefined'
3370
- && tmpSnapshotData.highlight_after_ms !== null
3371
- && tmpSnapshotData.highlight_after_ms > 0
3372
- && typeof tmpSnapshotData.highlight_before_ms !== 'undefined'
3373
- && tmpSnapshotData.highlight_before_ms !== null
3374
- && tmpSnapshotData.highlight_before_ms > 0
3375
- ) {
3376
- urlOptions.highlight_after = tmpSnapshotData.highlight_after_ms;
3377
- urlOptions.highlight_before = tmpSnapshotData.highlight_before_ms;
3378
- urlOptions.highlight = true;
3379
- }
3380
- else {
3381
- urlOptions.highlight_after = 0;
3382
- urlOptions.highlight_before = 0;
3383
- urlOptions.highlight = false;
3384
- }
3385
-
3386
- netdataCheckXSS = false; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded
3387
- NETDATA.xss.enabled = true; // we should not do any remote requests, but if we do, check them
3388
- NETDATA.xss.enabled_for_data = true; // check also snapshot data - that have been excluded from the initial check, due to compression
3389
- loadSnapshotPreflightEmpty();
3390
- initializeDynamicDashboard();
3391
- });
3392
- });
3393
- };
3394
-
3395
-
3396
- function loadSnapshotPreflightFile(file) {
3397
- var filename = NETDATA.xss.string(file.name);
3398
- var fr = new FileReader();
3399
- fr.onload = function(e) {
3400
- document.getElementById('loadSnapshotFilename').innerHTML = filename;
3401
- var result = null;
3402
- try {
3403
- result = NETDATA.xss.checkAlways('snapshot', JSON.parse(e.target.result), /^(snapshot\.info|snapshot\.data)$/);
3404
-
3405
- //console.log(result);
3406
- var date_after = new Date(result.after_ms);
3407
- var date_before = new Date(result.before_ms);
3408
-
3409
- if (typeof result.charts_ok === 'undefined')
3410
- result.charts_ok = 'unknown';
3411
-
3412
- if (typeof result.charts_failed === 'undefined')
3413
- result.charts_failed = 0;
3414
-
3415
- if (typeof result.compression === 'undefined')
3416
- result.compression = 'none';
3417
-
3418
- if (typeof result.data_size === 'undefined')
3419
- result.data_size = 0;
3420
-
3421
- document.getElementById('loadSnapshotFilename').innerHTML = '<code>' + filename + '</code>';
3422
- document.getElementById('loadSnapshotHostname').innerHTML = '<b>' + result.hostname + '</b>, netdata version: <b>' + result.netdata_version.toString() + '</b>';
3423
- document.getElementById('loadSnapshotURL').innerHTML = result.url;
3424
- document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point';
3425
- document.getElementById('loadSnapshotInfo').innerHTML = 'version: <b>' + result.snapshot_version.toString() + '</b>, includes <b>' + result.charts_ok.toString() + '</b> unique chart data queries ' + ((result.charts_failed > 0) ? ('<b>' + result.charts_failed.toString() + '</b> failed') : '').toString() + ', compressed with <code>' + result.compression.toString() + '</code>, data size ' + (Math.round(result.data_size * 100 / 1024 / 1024) / 100).toString() + ' MB';
3426
- document.getElementById('loadSnapshotTimeRange').innerHTML = '<b>' + NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + '</b> to <b>' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before) + '</b>';
3427
- document.getElementById('loadSnapshotComments').innerHTML = ((result.comments) ? result.comments : '').toString();
3428
- loadSnapshotModalLog('success', 'File loaded, click <b>Import</b> to render it!');
3429
- $('#loadSnapshotImport').removeClass('disabled');
3430
-
3431
- tmpSnapshotData = result;
3432
- }
3433
- catch (e) {
3434
- console.log(e);
3435
- document.getElementById('loadSnapshotStatus').className = "alert alert-danger";
3436
- document.getElementById('loadSnapshotStatus').innerHTML = "Failed to parse this file!";
3437
- $('#loadSnapshotImport').addClass('disabled');
3438
- }
3439
- }
3440
-
3441
- //console.log(file);
3442
- fr.readAsText(file);
3443
- };
3444
-
3445
- function loadSnapshotPreflightEmpty() {
3446
- document.getElementById('loadSnapshotFilename').innerHTML = '';
3447
- document.getElementById('loadSnapshotHostname').innerHTML = '';
3448
- document.getElementById('loadSnapshotURL').innerHTML = '';
3449
- document.getElementById('loadSnapshotCharts').innerHTML = '';
3450
- document.getElementById('loadSnapshotInfo').innerHTML = '';
3451
- document.getElementById('loadSnapshotTimeRange').innerHTML = '';
3452
- document.getElementById('loadSnapshotComments').innerHTML = '';
3453
- loadSnapshotModalLog('success', 'Browse for a snapshot file (or drag it and drop it here), then click <b>Import</b> to render it.');
3454
- $('#loadSnapshotImport').addClass('disabled');
3455
- };
3456
-
3457
- var loadSnapshotDragAndDropInitialized = false;
3458
- function loadSnapshotDragAndDropSetup() {
3459
- if(loadSnapshotDragAndDropInitialized === false) {
3460
- loadSnapshotDragAndDropInitialized = true;
3461
- $('#loadSnapshotDragAndDrop')
3462
- .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) {
3463
- e.preventDefault();
3464
- e.stopPropagation();
3465
- })
3466
- .on('drop', function (e) {
3467
- if(e.originalEvent.dataTransfer.files.length) {
3468
- loadSnapshotPreflightFile(e.originalEvent.dataTransfer.files.item(0));
3469
- }
3470
- else {
3471
- loadSnapshotPreflightEmpty();
3472
- loadSnapshotModalLog('danger', 'No file selected');
3473
- }
3474
- });
3475
- }
3476
- };
3477
-
3478
- function loadSnapshotPreflight() {
3479
- var files = document.getElementById('loadSnapshotSelectFiles').files;
3480
- if (files.length <= 0) {
3481
- loadSnapshotPreflightEmpty();
3482
- loadSnapshotModalLog('danger', 'No file selected');
3483
- return;
3484
- }
3485
-
3486
- loadSnapshotModalLog('info', 'Loading file...');
3487
-
3488
- loadSnapshotPreflightFile(files.item(0));
3489
- }
3490
-
3491
- // --------------------------------------------------------------------
3492
- // saving snapshots
3493
-
3494
- var saveSnapshotStop = false;
3495
- function saveSnapshotCancel() {
3496
- saveSnapshotStop = true;
3497
- }
3498
-
3499
- var saveSnapshotModalInitialized = false;
3500
- function saveSnapshotModalSetup() {
3501
- if(saveSnapshotModalInitialized === false) {
3502
- saveSnapshotModalInitialized = true;
3503
- $('#saveSnapshotModal')
3504
- .on('hide.bs.modal', saveSnapshotCancel)
3505
- .on('show.bs.modal', saveSnapshotModalInit)
3506
- .on('shown.bs.modal', function() {
3507
- $('#saveSnapshotResolutionSlider').find(".slider-handle:first").attr("tabindex", 1);
3508
- document.getElementById('saveSnapshotComments').focus();
3509
- });
3510
- }
3511
- };
3512
-
3513
- function saveSnapshotModalLog(priority, msg) {
3514
- document.getElementById('saveSnapshotStatus').className = "alert alert-" + priority;
3515
- document.getElementById('saveSnapshotStatus').innerHTML = msg;
3516
- }
3517
-
3518
- function saveSnapshotModalShowExpectedSize() {
3519
- var points = Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint);
3520
- var priority = 'info';
3521
- var msg = 'A moderate snapshot.';
3522
-
3523
- var sizemb = Math.round(
3524
- (options.data.charts_count * snapshotOptions.bytes_per_chart
3525
- + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_disk)
3526
- * 10 / 1024 / 1024) / 10;
3527
-
3528
- var memmb = Math.round(
3529
- (options.data.charts_count * snapshotOptions.bytes_per_chart
3530
- + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_memory)
3531
- * 10 / 1024 / 1024) / 10;
3532
-
3533
- if(sizemb < 10) {
3534
- priority = 'success';
3535
- msg = 'A nice small snapshot!';
3536
- }
3537
- if(sizemb > 50) {
3538
- priority = 'warning';
3539
- msg = 'Will stress your browser...';
3540
- }
3541
- if(sizemb > 100) {
3542
- priority = 'danger';
3543
- msg = 'Hm... good luck...';
3544
- }
3545
-
3546
- saveSnapshotModalLog(priority, 'The snapshot will have ' + points.toString() + ' points per dimension. Expected size on disk ' + sizemb + ' MB, at browser memory ' + memmb + ' MB.<br/>' + msg);
3547
- }
3548
-
3549
- var saveSnapshotCompression = snapshotOptions.compressionDefault;
3550
- function saveSnapshotSetCompression(name) {
3551
- saveSnapshotCompression = name;
3552
- document.getElementById('saveSnapshotCompressionName').innerHTML = saveSnapshotCompression;
3553
- saveSnapshotModalShowExpectedSize();
3554
- }
3555
-
3556
- var saveSnapshotSlider = null;
3557
- var saveSnapshotSelectedSecondsPerPoint = 1;
3558
- var saveSnapshotViewDuration = 1;
3559
- function saveSnapshotModalInit() {
3560
- $('#saveSnapshotModalProgressSection').hide();
3561
- $('#saveSnapshotResolutionRadio').show();
3562
- saveSnapshotModalLog('info', 'Select resolution and click <b>Save</b>');
3563
- $('#saveSnapshotExport').removeClass('disabled');
3564
-
3565
- loadBootstrapSlider(function() {
3566
- saveSnapshotViewDuration = options.duration;
3567
- var start_ms = Math.round(Date.now() - saveSnapshotViewDuration * 1000);
3568
-
3569
- if(NETDATA.globalPanAndZoom.isActive() === true) {
3570
- saveSnapshotViewDuration = Math.round((NETDATA.globalPanAndZoom.force_before_ms - NETDATA.globalPanAndZoom.force_after_ms) / 1000);
3571
- start_ms = NETDATA.globalPanAndZoom.force_after_ms;
3572
- }
3573
-
3574
- var start_date = new Date(start_ms);
3575
- var yyyymmddhhssmm = start_date.getFullYear() + NETDATA.zeropad(start_date.getMonth() + 1) + NETDATA.zeropad(start_date.getDate()) + '-' + NETDATA.zeropad(start_date.getHours()) + NETDATA.zeropad(start_date.getMinutes()) + NETDATA.zeropad(start_date.getSeconds());
3576
-
3577
- document.getElementById('saveSnapshotFilename').value = 'netdata-' + options.hostname.toString() + '-' + yyyymmddhhssmm.toString() + '-' + saveSnapshotViewDuration.toString() + '.snapshot';
3578
- saveSnapshotSetCompression(saveSnapshotCompression);
3579
-
3580
- var min = options.update_every;
3581
- var max = Math.round(saveSnapshotViewDuration / 100);
3582
-
3583
- if(NETDATA.globalPanAndZoom.isActive() === false)
3584
- max = Math.round(saveSnapshotViewDuration / 50);
3585
-
3586
- var view = Math.round(saveSnapshotViewDuration / Math.round($(document.getElementById('charts_div')).width() / 2));
3587
-
3588
- // console.log('view duration: ' + saveSnapshotViewDuration + ', min: ' + min + ', max: ' + max + ', view: ' + view);
3589
-
3590
- if(max < 10) max = 10;
3591
- if(max < min) max = min;
3592
- if(view < min) view = min;
3593
- if(view > max) view = max;
3594
-
3595
- if(saveSnapshotSlider !== null)
3596
- saveSnapshotSlider.destroy();
3597
-
3598
- saveSnapshotSlider = new Slider('#saveSnapshotResolutionSlider', {
3599
- ticks: [ min, view, max ],
3600
- min: min,
3601
- max: max,
3602
- step: options.update_every,
3603
- value: view,
3604
- scale: (max > 100)?'logarithmic':'linear',
3605
- tooltip: 'always',
3606
- formatter: function(value) {
3607
- if(value < 1)
3608
- value = 1;
3609
-
3610
- if(value < options.data.update_every)
3611
- value = options.data.update_every;
3612
-
3613
- saveSnapshotSelectedSecondsPerPoint = value;
3614
- saveSnapshotModalShowExpectedSize();
3615
-
3616
- var seconds = ' seconds ';
3617
- if(value === 1)
3618
- seconds = ' second ';
3619
-
3620
- return value + seconds + 'per point' + ((value === options.data.update_every)?', server default':'').toString();
3621
- }
3622
- });
3623
- });
3624
- }
3625
-
3626
- function saveSnapshot() {
3627
- loadPako(function () {
3628
- loadLzString(function () {
3629
- saveSnapshotStop = false;
3630
- $('#saveSnapshotModalProgressSection').show();
3631
- $('#saveSnapshotResolutionRadio').hide();
3632
- $('#saveSnapshotExport').addClass('disabled');
3633
-
3634
- var filename = document.getElementById('saveSnapshotFilename').value;
3635
- // console.log(filename);
3636
- saveSnapshotModalLog('info', 'Generating snapshot as <code>' + filename.toString() + '</code>');
3637
-
3638
- var save_options = {
3639
- stop_updates_when_focus_is_lost: false,
3640
- update_only_visible: false,
3641
- sync_selection: false,
3642
- eliminate_zero_dimensions: true,
3643
- pan_and_zoom_data_padding: false,
3644
- show_help: false,
3645
- legend_toolbox: false,
3646
- resize_charts: false,
3647
- pixels_per_point: 1
3648
- };
3649
- var backedup_options = {};
3650
-
3651
- var x;
3652
- for (x in save_options) {
3653
- if (save_options.hasOwnProperty(x)) {
3654
- backedup_options[x] = NETDATA.options.current[x];
3655
- NETDATA.options.current[x] = save_options[x];
3656
- }
3657
- }
3658
-
3659
- var el = document.getElementById('saveSnapshotModalProgressBar');
3660
- var eltxt = document.getElementById('saveSnapshotModalProgressBarText');
3661
-
3662
- options.data.charts_by_name = null;
3663
-
3664
- var saveData = {
3665
- hostname: options.hostname,
3666
- server: NETDATA.serverDefault,
3667
- netdata_version: options.data.version,
3668
- snapshot_version: 1,
3669
- after_ms: Date.now() - options.duration * 1000,
3670
- before_ms: Date.now(),
3671
- highlight_after_ms: urlOptions.highlight_after,
3672
- highlight_before_ms: urlOptions.highlight_before,
3673
- duration_ms: options.duration * 1000,
3674
- update_every_ms: options.update_every * 1000,
3675
- data_points: 0,
3676
- url: ((urlOptions.server !== null) ? urlOptions.server : document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString()).toString(),
3677
- comments: document.getElementById('saveSnapshotComments').value.toString(),
3678
- hash: urlOptions.hash,
3679
- charts: options.data,
3680
- info: jsonStringifyFn({
3681
- menu: netdataDashboard.menu,
3682
- submenu: netdataDashboard.submenu,
3683
- context: netdataDashboard.context
3684
- }),
3685
- charts_ok: 0,
3686
- charts_failed: 0,
3687
- compression: saveSnapshotCompression,
3688
- data_size: 0,
3689
- data: {}
3690
- };
3691
-
3692
- if(typeof snapshotOptions.compressions[saveData.compression] === 'undefined') {
3693
- alert('unknown compression method: ' + saveData.compression);
3694
- saveData.compression = 'none';
3695
- }
3696
-
3697
- var compress = snapshotOptions.compressions[saveData.compression].compress;
3698
- var compressed_length = snapshotOptions.compressions[saveData.compression].compressed_length;
3699
-
3700
- function pack_api1_v1_chart_data(state) {
3701
- if (state.library_name === null || state.data === null)
3702
- return;
3703
-
3704
- var data = state.data;
3705
- state.data = null;
3706
- data.state = null;
3707
- var str = JSON.stringify(data);
3708
-
3709
- if (typeof str === 'string') {
3710
- var cstr = compress(str);
3711
- saveData.data[state.chartDataUniqueID()] = cstr;
3712
- return compressed_length(cstr);
3713
- }
3714
- else
3715
- return 0;
3716
- }
3717
-
3718
- var clearPanAndZoom = false;
3719
- if (NETDATA.globalPanAndZoom.isActive() === false) {
3720
- NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveData.after_ms, saveData.before_ms);
3721
- clearPanAndZoom = true;
3722
- }
3723
-
3724
- saveData.after_ms = NETDATA.globalPanAndZoom.force_after_ms;
3725
- saveData.before_ms = NETDATA.globalPanAndZoom.force_before_ms;
3726
- saveData.duration_ms = saveData.before_ms - saveData.after_ms;
3727
- saveData.data_points = Math.round((saveData.before_ms - saveData.after_ms) / (saveSnapshotSelectedSecondsPerPoint * 1000));
3728
- saveSnapshotModalLog('info', 'Generating snapshot with ' + saveData.data_points.toString() + ' data points per dimension...');
3729
-
3730
- var charts_count = 0;
3731
- var charts_ok = 0;
3732
- var charts_failed = 0;
3733
-
3734
- function saveSnapshotRestore() {
3735
- $('#saveSnapshotModal').modal('hide');
3736
-
3737
- // restore the options
3738
- var x;
3739
- for (x in backedup_options) {
3740
- if (backedup_options.hasOwnProperty(x))
3741
- NETDATA.options.current[x] = backedup_options[x];
3742
- }
3743
-
3744
- $(el).css('width', '0%').attr('aria-valuenow', 0);
3745
- eltxt.innerText = '0%';
3746
-
3747
- if (clearPanAndZoom)
3748
- NETDATA.globalPanAndZoom.clearMaster();
3749
-
3750
- NETDATA.options.force_data_points = 0;
3751
- NETDATA.options.fake_chart_rendering = false;
3752
- NETDATA.onscroll_updater_enabled = true;
3753
- NETDATA.onresize();
3754
- NETDATA.unpause();
3755
-
3756
- $('#saveSnapshotExport').removeClass('disabled');
3757
- }
3758
-
3759
- NETDATA.globalSelectionSync.stop();
3760
- NETDATA.options.force_data_points = saveData.data_points;
3761
- NETDATA.options.fake_chart_rendering = true;
3762
- NETDATA.onscroll_updater_enabled = false;
3763
- NETDATA.abortAllRefreshes();
3764
-
3765
- var size = 0;
3766
- var info = ' Resolution: <b>' + saveSnapshotSelectedSecondsPerPoint.toString() + ((saveSnapshotSelectedSecondsPerPoint === 1)?' second ':' seconds ').toString() + 'per point</b>.';
3767
-
3768
- function update_chart(idx) {
3769
- if (saveSnapshotStop === true) {
3770
- saveSnapshotModalLog('info', 'Cancelled!');
3771
- saveSnapshotRestore();
3772
- return;
3773
- }
3774
-
3775
- var state = NETDATA.options.targets[--idx];
3776
-
3777
- var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length;
3778
- $(el).css('width', pcent + '%').attr('aria-valuenow', pcent);
3779
- eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id;
3780
-
3781
- setTimeout(function () {
3782
- charts_count++;
3783
- state.isVisible(true);
3784
- state.current.force_after_ms = saveData.after_ms;
3785
- state.current.force_before_ms = saveData.before_ms;
3786
-
3787
- state.updateChart(function (status, reason) {
3788
- state.current.force_after_ms = null;
3789
- state.current.force_before_ms = null;
3790
-
3791
- if (status === true) {
3792
- charts_ok++;
3793
- // state.log('ok');
3794
- size += pack_api1_v1_chart_data(state);
3795
- }
3796
- else {
3797
- charts_failed++;
3798
- state.log('failed to be updated: ' + reason);
3799
- }
3800
-
3801
- saveSnapshotModalLog((charts_failed) ? 'danger' : 'info', 'Generated snapshot data size <b>' + (Math.round(size * 100 / 1024 / 1024) / 100).toString() + ' MB</b>. ' + ((charts_failed) ? (charts_failed.toString() + ' charts have failed to be downloaded') : '').toString() + info);
3802
-
3803
- if (idx > 0) {
3804
- update_chart(idx);
3805
- }
3806
- else {
3807
- saveData.charts_ok = charts_ok;
3808
- saveData.charts_failed = charts_failed;
3809
- saveData.data_size = size;
3810
- // console.log(saveData.compression + ': ' + (size / (options.data.dimensions_count * Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint))).toString());
3811
-
3812
- // save it
3813
- // console.log(saveData);
3814
- saveObjectToClient(saveData, filename);
3815
-
3816
- if (charts_failed > 0)
3817
- alert(charts_failed.toString() + ' failed to be downloaded');
3818
-
3819
- saveSnapshotRestore();
3820
- saveData = null;
3821
- }
3822
- })
3823
- }, 0);
3824
- }
3825
-
3826
- update_chart(NETDATA.options.targets.length);
3827
- });
3828
- });
3829
- }
3830
-
3831
- // --------------------------------------------------------------------
3832
- // activate netdata on the page
3833
-
3834
- function dashboardSettingsSetup() {
3835
- var update_options_modal = function() {
3836
- // console.log('update_options_modal');
3837
-
3838
- var sync_option = function(option) {
3839
- var self = $('#' + option);
3840
-
3841
- if(self.prop('checked') !== NETDATA.getOption(option)) {
3842
- // console.log('switching ' + option.toString());
3843
- self.bootstrapToggle(NETDATA.getOption(option)?'on':'off');
3844
- }
3845
- };
3846
-
3847
- var theme_sync_option = function(option) {
3848
- var self = $('#' + option);
3849
-
3850
- self.bootstrapToggle(netdataTheme === 'slate'?'on':'off');
3851
- };
3852
- var units_sync_option = function(option) {
3853
- var self = $('#' + option);
3854
-
3855
- if(self.prop('checked') !== (NETDATA.getOption('units') === 'auto')) {
3856
- self.bootstrapToggle(NETDATA.getOption('units') === 'auto' ? 'on' : 'off');
3857
- }
3858
-
3859
- if(self.prop('checked') === true) {
3860
- $('#settingsLocaleTempRow').show();
3861
- $('#settingsLocaleTimeRow').show();
3862
- }
3863
- else {
3864
- $('#settingsLocaleTempRow').hide();
3865
- $('#settingsLocaleTimeRow').hide();
3866
- }
3867
- };
3868
- var temp_sync_option = function(option) {
3869
- var self = $('#' + option);
3870
-
3871
- if(self.prop('checked') !== (NETDATA.getOption('temperature') === 'celsius')) {
3872
- self.bootstrapToggle(NETDATA.getOption('temperature') === 'celsius' ? 'on' : 'off');
3873
- }
3874
- };
3875
- var timezone_sync_option = function(option) {
3876
- var self = $('#' + option);
3877
-
3878
- document.getElementById('browser_timezone').innerText = NETDATA.options.browser_timezone;
3879
- document.getElementById('server_timezone').innerText = NETDATA.options.server_timezone;
3880
- document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default')?'unset, using browser default':NETDATA.options.current.timezone;
3881
-
3882
- if(self.prop('checked') === NETDATA.dateTime.using_timezone) {
3883
- self.bootstrapToggle(NETDATA.dateTime.using_timezone ? 'off' : 'on');
3884
- }
3885
- };
3886
-
3887
- sync_option('eliminate_zero_dimensions');
3888
- sync_option('destroy_on_hide');
3889
- sync_option('async_on_scroll');
3890
- sync_option('parallel_refresher');
3891
- sync_option('concurrent_refreshes');
3892
- sync_option('sync_selection');
3893
- sync_option('sync_pan_and_zoom');
3894
- sync_option('stop_updates_when_focus_is_lost');
3895
- sync_option('smooth_plot');
3896
- sync_option('pan_and_zoom_data_padding');
3897
- sync_option('show_help');
3898
- sync_option('seconds_as_time');
3899
- theme_sync_option('netdata_theme_control');
3900
- units_sync_option('units_conversion');
3901
- temp_sync_option('units_temp');
3902
- timezone_sync_option('local_timezone');
3903
-
3904
- if(NETDATA.getOption('parallel_refresher') === false) {
3905
- $('#concurrent_refreshes_row').hide();
3906
- }
3907
- else {
3908
- $('#concurrent_refreshes_row').show();
3909
- }
3910
- };
3911
- NETDATA.setOption('setOptionCallback', update_options_modal);
3912
-
3913
- // handle options changes
3914
- $('#eliminate_zero_dimensions').change(function() { NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); });
3915
- $('#destroy_on_hide').change(function() { NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); });
3916
- $('#async_on_scroll').change(function() { NETDATA.setOption('async_on_scroll', $(this).prop('checked')); });
3917
- $('#parallel_refresher').change(function() { NETDATA.setOption('parallel_refresher', $(this).prop('checked')); });
3918
- $('#concurrent_refreshes').change(function() { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); });
3919
- $('#sync_selection').change(function() { NETDATA.setOption('sync_selection', $(this).prop('checked')); });
3920
- $('#sync_pan_and_zoom').change(function() { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); });
3921
- $('#stop_updates_when_focus_is_lost').change(function() {
3922
- urlOptions.update_always = !$(this).prop('checked');
3923
- urlOptions.hashUpdate();
3924
-
3925
- NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
3926
- });
3927
- $('#smooth_plot').change(function() { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
3928
- $('#pan_and_zoom_data_padding').change(function() { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
3929
- $('#seconds_as_time').change(function() { NETDATA.setOption('seconds_as_time', $(this).prop('checked')); });
3930
- $('#local_timezone').change(function() {
3931
- if($(this).prop('checked'))
3932
- selected_server_timezone('default', true);
3933
- else
3934
- selected_server_timezone('default', false);
3935
- });
3936
-
3937
-
3938
- $('#units_conversion').change(function() {
3939
- NETDATA.setOption('units', $(this).prop('checked')?'auto':'original');
3940
- });
3941
- $('#units_temp').change(function() {
3942
- NETDATA.setOption('temperature', $(this).prop('checked')?'celsius':'fahrenheit');
3943
- });
3944
-
3945
- $('#show_help').change(function() {
3946
- urlOptions.help = $(this).prop('checked');
3947
- urlOptions.hashUpdate();
3948
-
3949
- NETDATA.setOption('show_help', urlOptions.help);
3950
- netdataReload();
3951
- });
3952
-
3953
- // this has to be the last
3954
- // it reloads the page
3955
- $('#netdata_theme_control').change(function() {
3956
- urlOptions.theme = $(this).prop('checked')?'slate':'white';
3957
- urlOptions.hashUpdate();
3958
-
3959
- if(setTheme(urlOptions.theme))
3960
- netdataReload();
3961
- });
3962
- }
3963
-
3964
- function scrollDashboardTo() {
3965
- if(netdataSnapshotData !== null && typeof netdataSnapshotData.hash !== 'undefined') {
3966
- //console.log(netdataSnapshotData.hash);
3967
- scrollToId(netdataSnapshotData.hash.replace('#',''));
3968
- }
3969
- else {
3970
- // check if we have to jump to a specific section
3971
- scrollToId(urlOptions.hash.replace('#', ''));
3972
-
3973
- if (urlOptions.chart !== null) {
3974
- NETDATA.alarms.scrollToChart(urlOptions.chart);
3975
- //urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu);
3976
- //urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart);
3977
- //console.log('hash = ' + urlOptions.hash);
3978
- }
3979
- }
3980
- }
3981
-
3982
- var modalHiddenCallback = null;
3983
- function scrollToChartAfterHidingModal(chart) {
3984
- modalHiddenCallback = function() {
3985
- NETDATA.alarms.scrollToChart(chart);
3986
- };
3987
- }
3988
-
3989
- // ----------------------------------------------------------------------------
3990
-
3991
- function enableTooltipsAndPopovers() {
3992
- $('[data-toggle="tooltip"]').tooltip({
3993
- animated: 'fade',
3994
- trigger: 'hover',
3995
- html: true,
3996
- delay: {show: 500, hide: 0},
3997
- container: 'body'
3998
- });
3999
- $('[data-toggle="popover"]').popover();
4000
- }
4001
-
4002
- // ----------------------------------------------------------------------------
4003
-
4004
- var runOnceOnDashboardLastRun = 0;
4005
- function runOnceOnDashboardWithjQuery() {
4006
- if(runOnceOnDashboardLastRun !== 0) {
4007
- scrollDashboardTo();
4008
-
4009
- // restore the scrollspy at the proper position
4010
- $(document.body).scrollspy('refresh');
4011
- $(document.body).scrollspy('process');
4012
-
4013
- return;
4014
- }
4015
-
4016
- runOnceOnDashboardLastRun = Date.now();
4017
-
4018
- // ------------------------------------------------------------------------
4019
- // bootstrap modals
4020
-
4021
- // prevent bootstrap modals from scrolling the page
4022
- // maintains the current scroll position
4023
- // https://stackoverflow.com/a/34754029/4525767
4024
-
4025
- var scrollPos = 0;
4026
- var modal_depth = 0; // how many modals are currently open
4027
- var modal_shown = false; // set to true, if a modal is shown
4028
- var netdata_paused_on_modal = false; // set to true, if the modal paused netdata
4029
- var scrollspyOffset = $(window).height() / 3; // will be updated below - the offset of scrollspy to select an item
4030
-
4031
- $('.modal')
4032
- .on('show.bs.modal', function () {
4033
- if(modal_depth === 0) {
4034
- scrollPos = window.scrollY;
4035
-
4036
- $('body').css({
4037
- overflow: 'hidden',
4038
- position: 'fixed',
4039
- top: -scrollPos
4040
- });
4041
-
4042
- modal_shown = true;
4043
-
4044
- if (NETDATA.options.pauseCallback === null) {
4045
- NETDATA.pause(function () {});
4046
- netdata_paused_on_modal = true;
4047
- }
4048
- else
4049
- netdata_paused_on_modal = false;
4050
- }
4051
-
4052
- modal_depth++;
4053
- //console.log(urlOptions.after);
4054
-
4055
- })
4056
- .on('hide.bs.modal', function () {
4057
-
4058
- modal_depth--;
4059
-
4060
- if(modal_depth <= 0) {
4061
- modal_depth = 0;
4062
-
4063
- $('body')
4064
- .css({
4065
- overflow: '',
4066
- position: '',
4067
- top: ''
4068
- });
4069
-
4070
- // scroll to the position we had open before the modal
4071
- $('html, body')
4072
- .animate({scrollTop: scrollPos}, 0);
4073
-
4074
- // unpause netdata, if we paused it
4075
- if (netdata_paused_on_modal === true) {
4076
- NETDATA.unpause();
4077
- netdata_paused_on_modal = false;
4078
- }
4079
-
4080
- // restore the scrollspy at the proper position
4081
- $(document.body).scrollspy('process');
4082
- }
4083
- //console.log(urlOptions.after);
4084
- })
4085
- .on('hidden.bs.modal', function () {
4086
- if(modal_depth === 0)
4087
- modal_shown = false;
4088
-
4089
- if(typeof modalHiddenCallback === 'function')
4090
- modalHiddenCallback();
4091
-
4092
- modalHiddenCallback = null;
4093
- //console.log(urlOptions.after);
4094
- });
4095
-
4096
- // ------------------------------------------------------------------------
4097
- // sidebar / affix
4098
-
4099
- $('#sidebar')
4100
- .affix({
4101
- offset: {
4102
- top: (isdemo())?150:0,
4103
- bottom: 0
4104
- }
4105
- })
4106
- .on('affixed.bs.affix', function() {
4107
- // fix scrolling of very long affix lists
4108
- // http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long
4109
-
4110
- $(this).removeAttr('style');
4111
- })
4112
- .on( 'affix-top.bs.affix', function() {
4113
- // fix bootstrap affix click bug
4114
- // https://stackoverflow.com/a/37847981/4525767
4115
-
4116
- if(modal_shown) return false;
4117
- })
4118
- .on('activate.bs.scrollspy', function (e) {
4119
- // change the URL based on the current position of the screen
4120
-
4121
- if(modal_shown === false) {
4122
- var el = $(e.target);
4123
- var hash = el.find('a').attr('href');
4124
- if (typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) {
4125
- urlOptions.hash = hash;
4126
- urlOptions.hashUpdate();
4127
- }
4128
- }
4129
- });
4130
-
4131
- Ps.initialize(document.getElementById('sidebar'), {
4132
- wheelSpeed: 0.5,
4133
- wheelPropagation: true,
4134
- swipePropagation: true,
4135
- minScrollbarLength: null,
4136
- maxScrollbarLength: null,
4137
- useBothWheelAxes: false,
4138
- suppressScrollX: true,
4139
- suppressScrollY: false,
4140
- scrollXMarginOffset: 0,
4141
- scrollYMarginOffset: 0,
4142
- theme: 'default'
4143
- });
4144
-
4145
-
4146
- // ------------------------------------------------------------------------
4147
- // scrollspy
4148
-
4149
- if(scrollspyOffset > 250) scrollspyOffset = 250;
4150
- if(scrollspyOffset < 75) scrollspyOffset = 75;
4151
- document.body.setAttribute('data-offset', scrollspyOffset);
4152
-
4153
- // scroll the dashboard, before activating the scrollspy, so that our
4154
- // hash will not be updated before we got the chance to scroll to it
4155
- scrollDashboardTo();
4156
-
4157
- $(document.body).scrollspy({
4158
- target: '#sidebar',
4159
- offset: scrollspyOffset // controls the diff of the <hX> element to the top, to select it
4160
- });
4161
-
4162
-
4163
- // ------------------------------------------------------------------------
4164
- // my-netdata menu
4165
-
4166
- Ps.initialize(document.getElementById('myNetdataDropdownUL'), {
4167
- wheelSpeed: 1,
4168
- wheelPropagation: false,
4169
- swipePropagation: false,
4170
- minScrollbarLength: null,
4171
- maxScrollbarLength: null,
4172
- useBothWheelAxes: false,
4173
- suppressScrollX: true,
4174
- suppressScrollY: false,
4175
- scrollXMarginOffset: 0,
4176
- scrollYMarginOffset: 0,
4177
- theme: 'default'
4178
- });
4179
-
4180
- $('#myNetdataDropdownParent')
4181
- .on('show.bs.dropdown', function () {
4182
- var hash = urlOptions.genHash();
4183
- $('.registry_link').each(function(idx) {
4184
- this.setAttribute('href', this.getAttribute("href").replace(/#.*$/, hash));
4185
- });
4186
-
4187
- NETDATA.pause(function() {});
4188
- })
4189
- .on('shown.bs.dropdown', function () {
4190
- Ps.update(document.getElementById('myNetdataDropdownUL'));
4191
- })
4192
- .on('hidden.bs.dropdown', function () {
4193
- NETDATA.unpause();
4194
- });
4195
-
4196
-
4197
- $('#deleteRegistryModal')
4198
- .on('hidden.bs.modal', function() {
4199
- deleteRegistryGuid = null;
4200
- });
4201
-
4202
-
4203
- // ------------------------------------------------------------------------
4204
- // update modal
4205
-
4206
- $('#updateModal')
4207
- .on('show.bs.modal', function() {
4208
- versionLog('checking, please wait...');
4209
- })
4210
- .on('shown.bs.modal', function() {
4211
- notifyForUpdate(true);
4212
- });
4213
-
4214
-
4215
- // ------------------------------------------------------------------------
4216
- // alarms modal
4217
-
4218
- $('#alarmsModal')
4219
- .on('shown.bs.modal', function() {
4220
- alarmsUpdateModal();
4221
- })
4222
- .on('hidden.bs.modal', function() {
4223
- document.getElementById('alarms_active').innerHTML =
4224
- document.getElementById('alarms_all').innerHTML =
4225
- document.getElementById('alarms_log').innerHTML =
4226
- 'loading...';
4227
- });
4228
-
4229
-
4230
- // ------------------------------------------------------------------------
4231
-
4232
- dashboardSettingsSetup();
4233
- loadSnapshotDragAndDropSetup();
4234
- saveSnapshotModalSetup();
4235
- showPageFooter();
4236
-
4237
-
4238
- // ------------------------------------------------------------------------
4239
- // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js
4240
-
4241
- $.fn.shorten = function(settings) {
4242
- "use strict";
4243
-
4244
- var config = {
4245
- showChars: 750,
4246
- minHideChars: 10,
4247
- ellipsesText: "...",
4248
- moreText: '<i class="fas fa-expand"></i> show more information',
4249
- lessText: '<i class="fas fa-compress"></i> show less information',
4250
- onLess: function() { NETDATA.onscroll(); },
4251
- onMore: function() { NETDATA.onscroll(); },
4252
- errMsg: null,
4253
- force: false
4254
- };
4255
-
4256
- if (settings) {
4257
- $.extend(config, settings);
4258
- }
4259
-
4260
- if ($(this).data('jquery.shorten') && !config.force) {
4261
- return false;
4262
- }
4263
- $(this).data('jquery.shorten', true);
4264
-
4265
- $(document).off("click", '.morelink');
4266
-
4267
- $(document).on({
4268
- click: function() {
4269
-
4270
- var $this = $(this);
4271
- if ($this.hasClass('less')) {
4272
- $this.removeClass('less');
4273
- $this.html(config.moreText);
4274
- $this.parent().prev().animate({'height':'0'+'%'}, 0, function () { $this.parent().prev().prev().show(); }).hide(0, function() {
4275
- config.onLess();
4276
- });
4277
-
4278
- } else {
4279
- $this.addClass('less');
4280
- $this.html(config.lessText);
4281
- $this.parent().prev().animate({'height':'100'+'%'}, 0, function () { $this.parent().prev().prev().hide(); }).show(0, function() {
4282
- config.onMore();
4283
- });
4284
- }
4285
- return false;
4286
- }
4287
- }, '.morelink');
4288
-
4289
- return this.each(function() {
4290
- var $this = $(this);
4291
-
4292
- var content = $this.html();
4293
- var contentlen = $this.text().length;
4294
- if (contentlen > config.showChars + config.minHideChars) {
4295
- var c = content.substr(0, config.showChars);
4296
- if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
4297
- {
4298
- var inTag = false; // I'm in a tag?
4299
- var bag = ''; // Put the characters to be shown here
4300
- var countChars = 0; // Current bag size
4301
- var openTags = []; // Stack for opened tags, so I can close them later
4302
- var tagName = null;
4303
-
4304
- for (var i = 0, r = 0; r <= config.showChars; i++) {
4305
- if (content[i] === '<' && !inTag) {
4306
- inTag = true;
4307
-
4308
- // This could be "tag" or "/tag"
4309
- tagName = content.substring(i + 1, content.indexOf('>', i));
4310
-
4311
- // If its a closing tag
4312
- if (tagName[0] === '/') {
4313
-
4314
-
4315
- if (tagName !== ('/' + openTags[0])) {
4316
- config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes';
4317
- } else {
4318
- openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!)
4319
- }
4320
-
4321
- } else {
4322
- // There are some nasty tags that don't have a close tag like <br/>
4323
- if (tagName.toLowerCase() !== 'br') {
4324
- openTags.unshift(tagName); // Add to start the name of the tag that opens
4325
- }
4326
- }
4327
- }
4328
- if (inTag && content[i] === '>') {
4329
- inTag = false;
4330
- }
4331
-
4332
- if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result
4333
- else {
4334
- r++;
4335
- if (countChars <= config.showChars) {
4336
- bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
4337
- countChars++;
4338
- } else // Now I have the characters needed
4339
- {
4340
- if (openTags.length > 0) // I have unclosed tags
4341
- {
4342
- //console.log('They were open tags');
4343
- //console.log(openTags);
4344
- for (var j = 0; j < openTags.length; j++) {
4345
- //console.log('Cierro tag ' + openTags[j]);
4346
- bag += '</' + openTags[j] + '>'; // Close all tags that were opened
4347
-
4348
- // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
4349
- }
4350
- break;
4351
- }
4352
- }
4353
- }
4354
- }
4355
- c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html();
4356
- }else{
4357
- c+=config.ellipsesText;
4358
- }
4359
-
4360
- var html = '<div class="shortcontent">' + c +
4361
- '</div><div class="allcontent">' + content +
4362
- '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>';
4363
-
4364
- $this.html(html);
4365
- $this.find(".allcontent").hide(); // Hide all text
4366
- $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
4367
- }
4368
- });
4369
- };
4370
- }
4371
-
4372
- function finalizePage() {
4373
- // resize all charts - without starting the background thread
4374
- // this has to be done while NETDATA is paused
4375
- // if we ommit this, the affix menu will be wrong, since all
4376
- // the Dom elements are initially zero-sized
4377
- NETDATA.parseDom();
4378
-
4379
- // ------------------------------------------------------------------------
4380
-
4381
- NETDATA.globalPanAndZoom.callback = null;
4382
- NETDATA.globalChartUnderlay.callback = null;
4383
-
4384
- if(urlOptions.pan_and_zoom === true && NETDATA.options.targets.length > 0)
4385
- NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
4386
-
4387
- // callback for us to track PanAndZoom operations
4388
- NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback;
4389
- NETDATA.globalChartUnderlay.callback = urlOptions.netdataHighlightCallback;
4390
-
4391
- // ------------------------------------------------------------------------
4392
-
4393
- // let it run (update the charts)
4394
- NETDATA.unpause();
4395
-
4396
- runOnceOnDashboardWithjQuery();
4397
- $(".shorten").shorten();
4398
- enableTooltipsAndPopovers();
4399
-
4400
- if(isdemo()) {
4401
- // do not to give errors on netdata demo servers for 60 seconds
4402
- NETDATA.options.current.retries_on_data_failures = 60;
4403
-
4404
- if(urlOptions.nowelcome !== true) {
4405
- setTimeout(function() {
4406
- $('#welcomeModal').modal();
4407
- }, 1000);
4408
- }
4409
-
4410
- // google analytics when this is used for the home page of the demo sites
4411
- // this does not run on user's installations
4412
- setTimeout(function() {
4413
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
4414
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
4415
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
4416
- })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
4417
-
4418
- ga('create', 'UA-64295674-3', 'auto');
4419
- ga('send', 'pageview');
4420
- }, 2000);
4421
- }
4422
- else notifyForUpdate();
4423
-
4424
- if(urlOptions.show_alarms === true)
4425
- setTimeout(function() { $('#alarmsModal').modal('show'); }, 1000);
4426
-
4427
- NETDATA.onresizeCallback = function() {
4428
- Ps.update(document.getElementById('sidebar'));
4429
- Ps.update(document.getElementById('myNetdataDropdownUL'));
4430
- };
4431
- NETDATA.onresizeCallback();
4432
-
4433
- if(netdataSnapshotData !== null) {
4434
- NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], netdataSnapshotData.after_ms, netdataSnapshotData.before_ms);
4435
- }
4436
-
4437
- // var netdataEnded = performance.now();
4438
- // console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms');
4439
- }
4440
-
4441
- function resetDashboardOptions() {
4442
- var help = NETDATA.options.current.show_help;
4443
-
4444
- NETDATA.resetOptions();
4445
- if(setTheme('slate'))
4446
- netdataReload();
4447
-
4448
- if(help !== NETDATA.options.current.show_help)
4449
- netdataReload();
4450
- }
4451
-
4452
- // callback to add the dashboard info to the
4453
- // parallel javascript downloader in netdata
4454
- var netdataPrepCallback = function() {
4455
- NETDATA.requiredCSS.push({
4456
- url: NETDATA.serverStatic + 'css/bootstrap-toggle-2.2.2.min.css',
4457
- isAlreadyLoaded: function() { return false; }
4458
- });
4459
-
4460
- NETDATA.requiredJs.push({
4461
- url: NETDATA.serverStatic + 'lib/bootstrap-toggle-2.2.2.min.js',
4462
- isAlreadyLoaded: function() { return false; }
4463
- });
4464
-
4465
- NETDATA.requiredJs.push({
4466
- url: NETDATA.serverStatic + 'dashboard_info.js?v20181019-1',
4467
- async: false,
4468
- isAlreadyLoaded: function() { return false; }
4469
- });
4470
-
4471
- if(isdemo()) {
4472
- document.getElementById('masthead').style.display = 'block';
4473
- }
4474
- else {
4475
- if(urlOptions.update_always === true)
4476
- NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
4477
- }
4478
- };
4479
-
4480
- var selected_server_timezone = function(timezone, status) {
4481
- //console.log('called with timezone: ' + timezone + ", status: " + ((typeof status === 'undefined')?'undefined':status).toString());
4482
-
4483
- // clear the error
4484
- document.getElementById('timezone_error_message').innerHTML = '';
4485
-
4486
- if (typeof status === 'undefined') {
4487
- // the user selected a timezone from the menu
4488
-
4489
- NETDATA.setOption('user_set_server_timezone', timezone);
4490
-
4491
- if (NETDATA.dateTime.init(timezone) === false) {
4492
- NETDATA.dateTime.init();
4493
-
4494
- if(!$('#local_timezone').prop('checked'))
4495
- $('#local_timezone').bootstrapToggle('on');
4496
-
4497
- document.getElementById('timezone_error_message').innerHTML = 'Ooops! That timezone was not accepted by your browser. Please open a github issue to help us fix it.';
4498
- NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone);
4499
- }
4500
- else {
4501
- if($('#local_timezone').prop('checked'))
4502
- $('#local_timezone').bootstrapToggle('off');
4503
- }
4504
- }
4505
- else if (status === true) {
4506
- // the user wants the browser default timezone to be activated
4507
-
4508
- NETDATA.dateTime.init();
4509
- }
4510
- else {
4511
- // the user wants the server default timezone to be activated
4512
- //console.log('found ' + NETDATA.options.current.user_set_server_timezone);
4513
-
4514
- if (NETDATA.options.current.user_set_server_timezone === 'default')
4515
- NETDATA.options.current.user_set_server_timezone = NETDATA.options.server_timezone;
4516
-
4517
- timezone = NETDATA.options.current.user_set_server_timezone;
4518
-
4519
- if (NETDATA.dateTime.init(timezone) === false) {
4520
- NETDATA.dateTime.init();
4521
-
4522
- if(!$('#local_timezone').prop('checked'))
4523
- $('#local_timezone').bootstrapToggle('on');
4524
-
4525
- document.getElementById('timezone_error_message').innerHTML = 'Sorry. The timezone "' + timezone.toString() + '" is not accepted by your browser. Please select one from the list.';
4526
- NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone);
4527
- }
4528
- }
4529
-
4530
- document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default')?'unset, using browser default':NETDATA.options.current.timezone;
4531
- return false;
4532
- };
4533
-
4534
- // our entry point
4535
- // var netdataStarted = performance.now();
4536
-
4537
- var netdataCallback = initializeDynamicDashboard;
4538
- </script>
53
+ <script src="main.js"></script>
54
</head>
55
56
<body data-spy="scroll" data-target="#sidebar" data-offset="100">
@@ -4543,8 +58,17 @@
58
netdata<br/><div style="font-size: 3vh;">Real-time performance monitoring, done right!</div>
59
</div>
60
<script type="text/javascript">
4546
- // change the loadOverlay colors ASAP to match the theme
4547
- document.getElementById('loadOverlay').style = (urlOptions.theme === 'slate')?"background-color:#272b30; color: #373b40;":"background-color:#fff; color: #ddd;";
61
+ // Change the loadOverlay colors ASAP to match the theme.
62
+ let theme;
63
+ const hash = document.location.hash;
64
+ if (hash.includes('theme=slate')) {
65
+ theme = 'slate';
66
+ } else if (hash.includes('theme=white')) {
67
+ theme = 'white';
68
+ } else {
69
+ theme = localStorage.getItem('netdataTheme') || 'slate';
70
+ }
71
+ document.getElementById('loadOverlay').style = theme == 'slate' ? "background-color: #272b30; color: #373b40;" : "background-color: #fff; color: #ddd;";
72
</script>
73
<nav class="navbar navbar-default navbar-fixed-top" role="banner">
74
<div class="container">
web/gui/main.css
new
+533
@@ -0,0 +1,533 @@
1
+/* force the vertical window scrollbar */
2
+html {
3
+ overflow-y: scroll;
4
+}
5
+
6
+/* prevent body from hiding under the navbar */
7
+body {
8
+ padding-top: 50px;
9
+}
10
+
11
+.loadOverlay {
12
+ position: absolute;
13
+ top: 0px;
14
+ left: 0px;
15
+ width: 100%;
16
+ height: 100%;
17
+ z-index: 2000;
18
+ font-size: 10vh;
19
+ font-family: sans-serif;
20
+ padding: 40vh 0 40vh 0;
21
+ font-weight: bold;
22
+ text-align: center;
23
+}
24
+
25
+.navbar-highlight {
26
+ display: none;
27
+ position: fixed;
28
+ margin-top: 5px;
29
+ height: 26px;
30
+ width: 100%;
31
+ text-align: center;
32
+ overflow: hidden;
33
+ z-index: 30;
34
+ pointer-events: none !important;
35
+}
36
+
37
+.navbar-highlight-content {
38
+ position: relative;
39
+ display: inline-block;
40
+ margin: 0 auto;
41
+ height: 26px;
42
+ min-width: 500px;
43
+ background-color: rgba(0, 0, 0, 0.7);
44
+ padding-top: 2px;
45
+ padding-bottom: 2px;
46
+ padding-left: 15px;
47
+ padding-right: 15px;
48
+ border-radius: 10px;
49
+ color: lightgrey;
50
+ pointer-events: auto !important;
51
+}
52
+
53
+.navbar-highlight-bar {
54
+ cursor: pointer;
55
+}
56
+
57
+.navbar-highlight-button-right {
58
+ cursor: pointer;
59
+ padding-left: 10px;
60
+}
61
+
62
+.modal-wide .modal-dialog {
63
+ width: 80%;
64
+}
65
+
66
+/* fix # anchors scrolling under the navbar
67
+ https://github.com/twbs/bootstrap/issues/1768#issuecomment-46519033
68
+ */
69
+h1 {
70
+ position: relative;
71
+ z-index: -1;
72
+}
73
+
74
+h2 {
75
+ position: relative;
76
+ z-index: -2;
77
+}
78
+
79
+h1:before, h2:before {
80
+ display: block;
81
+ content: " ";
82
+ margin-top: -70px;
83
+ height: 70px;
84
+ visibility: hidden;
85
+}
86
+
87
+.p {
88
+ display: block;
89
+ margin-top: 15px;
90
+}
91
+
92
+.option-row,
93
+.option-control {
94
+ vertical-align: top;
95
+ padding: 10px;
96
+ padding-top: 30px;
97
+ padding-left: 30px;
98
+}
99
+
100
+.option-info {
101
+ padding: 10px;
102
+}
103
+
104
+.dashboard-submenu-info {
105
+ display: block;
106
+ margin-top: 10px;
107
+}
108
+
109
+.dashboard-context-info {
110
+ display: block;
111
+ margin-top: 10px;
112
+}
113
+
114
+#masthead h1 {
115
+ /*font-size: 30px;*/
116
+ line-height: 1;
117
+ padding-top: 30px;
118
+}
119
+
120
+#masthead .well {
121
+ margin-top: 4%;
122
+}
123
+
124
+/* fix the navbar shifting when a modal is open */
125
+/* https://github.com/twbs/bootstrap/issues/14040#issuecomment-159891033 */
126
+body.modal-open {
127
+ width: 100% !important;
128
+ padding-right: 0 !important;
129
+ /* overflow-y: scroll !important; */
130
+ /* position: fixed !important;*/
131
+ overflow: visible;
132
+}
133
+
134
+/* make accordion use the whole header bar for expand/collapse */
135
+.panel-title a {
136
+ display: block;
137
+ padding: 10px 15px;
138
+ margin: -10px -15px;
139
+}
140
+
141
+/*
142
+ * Side navigation
143
+ *
144
+ * Scrollspy and affixed enhanced navigation to highlight sections and secondary
145
+ * sections of docs content.
146
+ */
147
+
148
+.affix {
149
+ position: static;
150
+ top: 70px !important;
151
+ /*width: 220px;*/
152
+}
153
+
154
+/*
155
+.affix-top {
156
+ width: 220px;
157
+}
158
+*/
159
+
160
+.dashboard-sidebar {
161
+ max-height: calc(100% - 70px) !important;
162
+ overflow-y: auto;
163
+ /*width: 220px !important;*/
164
+}
165
+
166
+/* By default it's not affixed in mobile views, so undo that */
167
+.dashboard-sidebar.affix {
168
+ position: static;
169
+}
170
+
171
+@media (min-width: 768px) {
172
+ .dashboard-sidebar {
173
+ padding-left: 20px;
174
+ }
175
+}
176
+
177
+/* First level of nav */
178
+.dashboard-sidenav {
179
+ margin-top: 20px;
180
+ margin-bottom: 20px;
181
+}
182
+
183
+/* All levels of nav */
184
+.dashboard-sidebar .nav > li > a {
185
+ display: block;
186
+ padding: 4px 20px;
187
+ font-size: 13px;
188
+ font-weight: 500;
189
+ color: #767676;
190
+}
191
+
192
+.dashboard-sidebar .nav > li > a > .svg-inline--fa {
193
+ width: 20px;
194
+ text-align: center;
195
+}
196
+
197
+.dashboard-sidebar .nav > li > a:hover,
198
+.dashboard-sidebar .nav > li > a:focus {
199
+ padding-left: 19px;
200
+ color: #563d7c;
201
+ text-decoration: none;
202
+ background-color: transparent;
203
+ border-left: 1px solid #563d7c;
204
+}
205
+
206
+.dashboard-sidebar .nav > .active > a,
207
+.dashboard-sidebar .nav > .active:hover > a,
208
+.dashboard-sidebar .nav > .active:focus > a {
209
+ padding-left: 18px;
210
+ font-weight: bold;
211
+ color: #563d7c;
212
+ background-color: transparent;
213
+ border-left: 2px solid #563d7c;
214
+}
215
+
216
+/* Nav: second level (shown on .active) */
217
+.dashboard-sidebar .nav .nav {
218
+ display: none; /* Hide by default, but at >768px, show it */
219
+ padding-bottom: 10px;
220
+}
221
+
222
+.dashboard-sidebar .nav .nav > li > a {
223
+ padding-top: 1px;
224
+ padding-bottom: 1px;
225
+ padding-left: 30px;
226
+ font-size: 12px;
227
+ font-weight: normal;
228
+}
229
+
230
+.dashboard-sidebar .nav .nav > li > a:hover,
231
+.dashboard-sidebar .nav .nav > li > a:focus {
232
+ padding-left: 29px;
233
+}
234
+
235
+.dashboard-sidebar .nav .nav > .active > a,
236
+.dashboard-sidebar .nav .nav > .active:hover > a,
237
+.dashboard-sidebar .nav .nav > .active:focus > a {
238
+ padding-left: 28px;
239
+ font-weight: 500;
240
+}
241
+
242
+.dropdown-menu {
243
+ min-width: 200px;
244
+}
245
+
246
+.dropdown-menu.columns-2 {
247
+ margin: 0;
248
+ padding: 0;
249
+ width: 400px;
250
+}
251
+
252
+.dropdown-menu li a {
253
+ padding: 5px 15px;
254
+ font-weight: 300;
255
+}
256
+
257
+.dropdown-menu.multi-column {
258
+ overflow-x: hidden;
259
+}
260
+
261
+.multi-column-dropdown {
262
+ list-style: none;
263
+ padding: 0;
264
+}
265
+
266
+.multi-column-dropdown li a {
267
+ display: block;
268
+ clear: both;
269
+ line-height: 1.428571429;
270
+ white-space: normal;
271
+}
272
+
273
+.multi-column-dropdown li a:hover {
274
+ text-decoration: none;
275
+ color: #f5f5f5;
276
+ background-color: #262626;
277
+}
278
+
279
+.scrollable-menu {
280
+ height: auto;
281
+ max-height: 80vh;
282
+ overflow-x: hidden;
283
+}
284
+
285
+.scrollable-menu-50 {
286
+ height: auto;
287
+ max-height: 50vh;
288
+ overflow-x: hidden;
289
+}
290
+
291
+/* Back to top (hidden on mobile) */
292
+.back-to-top,
293
+.dashboard-theme-toggle {
294
+ display: none;
295
+ padding: 4px 10px;
296
+ margin-top: 10px;
297
+ margin-left: 10px;
298
+ font-size: 12px;
299
+ font-weight: 500;
300
+ color: #999;
301
+}
302
+
303
+.back-to-top:hover,
304
+.dashboard-theme-toggle:hover {
305
+ color: #563d7c;
306
+ text-decoration: none;
307
+}
308
+
309
+.dashboard-theme-toggle {
310
+ margin-top: 0;
311
+}
312
+
313
+.container {
314
+ width: calc(100% - 20px) !important;
315
+}
316
+
317
+.charts-body {
318
+ display: inline-block;
319
+ width: 100%;
320
+}
321
+
322
+.sidebar-body {
323
+ position: absolute;
324
+ display: none;
325
+}
326
+
327
+.dashboard-section-container {
328
+ display: block;
329
+ width: 100%;
330
+ page-break-before: auto;
331
+ page-break-after: auto;
332
+ page-break-inside: auto;
333
+}
334
+
335
+.dashboard-print-row {
336
+ display: block;
337
+ width: 100%;
338
+ page-break-before: auto;
339
+ page-break-after: auto;
340
+ page-break-inside: avoid;
341
+}
342
+
343
+.netdata-chartblock-container {
344
+ display: inline-block;
345
+}
346
+
347
+/* https://github.com/seiyria/bootstrap-slider/issues/746 */
348
+.tooltip {
349
+ -webkit-user-select: none;
350
+ -moz-user-select: none;
351
+ -ms-user-select: none;
352
+ user-select: none;
353
+}
354
+
355
+@media print {
356
+ body {
357
+ overflow: visible !important;
358
+ -webkit-print-color-adjust: exact;
359
+ page-break-inside: auto;
360
+ page-break-before: auto;
361
+ page-break-after: auto;
362
+ }
363
+
364
+ .dashboard-section {
365
+ page-break-inside: auto;
366
+ page-break-before: auto;
367
+ page-break-after: auto;
368
+ }
369
+
370
+ .dashboard-subsection {
371
+ page-break-before: avoid;
372
+ page-break-after: auto;
373
+ page-break-inside: auto;
374
+ }
375
+
376
+ .charts-body {
377
+ padding-left: 0%;
378
+ padding-right: 0%;
379
+ display: block;
380
+ page-break-inside: auto;
381
+ page-break-before: auto;
382
+ page-break-after: auto;
383
+ }
384
+
385
+ .back-to-top,
386
+ .dashboard-theme-toggle {
387
+ display: block;
388
+ }
389
+}
390
+
391
+@media (min-width: 768px) {
392
+ .charts-body {
393
+ padding-left: 0%;
394
+ padding-right: 0%;
395
+ }
396
+
397
+ .back-to-top,
398
+ .dashboard-theme-toggle {
399
+ display: block;
400
+ }
401
+}
402
+
403
+/* Show and affix the side nav when space allows it */
404
+@media (min-width: 992px) {
405
+ .container {
406
+ padding-left: 0% !important;
407
+ }
408
+
409
+ .charts-body {
410
+ width: calc(100% - 213px) !important;
411
+ padding-left: 1% !important;
412
+ padding-right: 0% !important;
413
+ }
414
+
415
+ .sidebar-body {
416
+ display: inline-block !important;
417
+ width: 213px !important;
418
+ }
419
+
420
+ .dashboard-sidebar .nav > .active > ul {
421
+ display: block;
422
+ }
423
+
424
+ /* Widen the fixed sidebar */
425
+ .dashboard-sidebar.affix,
426
+ .dashboard-sidebar.affix-top,
427
+ .dashboard-sidebar.affix-bottom {
428
+ width: 213px !important;
429
+ }
430
+
431
+ .dashboard-sidebar.affix {
432
+ position: fixed; /* Undo the static from mobile first approach */
433
+ top: 20px;
434
+ }
435
+
436
+ .dashboard-sidebar.affix-bottom {
437
+ position: absolute; /* Undo the static from mobile first approach */
438
+ }
439
+
440
+ .dashboard-sidebar.affix-bottom .dashboard-sidenav,
441
+ .dashboard-sidebar.affix .dashboard-sidenav {
442
+ margin-top: 0;
443
+ margin-bottom: 0;
444
+ }
445
+}
446
+
447
+@media (min-width: 1200px) {
448
+ .container {
449
+ padding-left: 2% !important;
450
+ }
451
+
452
+ .charts-body {
453
+ width: calc(100% - 233px) !important;
454
+ padding-left: 1% !important;
455
+ padding-right: 1% !important;
456
+ }
457
+
458
+ .sidebar-body {
459
+ display: inline-block !important;
460
+ width: 233px !important;
461
+ }
462
+
463
+ /* Widen the fixed sidebar again */
464
+ .dashboard-sidebar.affix,
465
+ .dashboard-sidebar.affix-top,
466
+ .dashboard-sidebar.affix-bottom {
467
+ width: 233px !important;
468
+ }
469
+}
470
+
471
+@media (min-width: 1360px) {
472
+ .container {
473
+ padding-left: 3% !important;
474
+ }
475
+
476
+ .charts-body {
477
+ width: calc(100% - 263px) !important;
478
+ padding-left: 1% !important;
479
+ padding-right: 2% !important;
480
+ }
481
+
482
+ .sidebar-body {
483
+ display: inline-block !important;
484
+ width: 263px !important;
485
+ }
486
+
487
+ /* Widen the fixed sidebar again */
488
+ .dashboard-sidebar.affix,
489
+ .dashboard-sidebar.affix-top,
490
+ .dashboard-sidebar.affix-bottom {
491
+ width: 263px !important;
492
+ }
493
+}
494
+
495
+.action-button {
496
+ position: relative;
497
+ display: inline-block;
498
+ color: gray;
499
+ cursor: pointer;
500
+ margin: 0 auto;
501
+ width: 30px;
502
+ height: 30px;
503
+ font-size: 25px;
504
+}
505
+
506
+.ripple {
507
+ position: relative;
508
+ /*overflow: hidden;*/
509
+ transform: translate3d(0, 0, 0)
510
+}
511
+
512
+.ripple:after {
513
+ content: "";
514
+ display: block;
515
+ position: absolute;
516
+ width: 100%;
517
+ height: 100%;
518
+ top: 0;
519
+ left: 0;
520
+ pointer-events: none;
521
+ background-image: radial-gradient(circle, #000 10%, transparent 10.01%);
522
+ background-repeat: no-repeat;
523
+ background-position: 50%;
524
+ transform: scale(18, 18); /* the size of the ripple */
525
+ opacity: 0;
526
+ transition: transform .5s, opacity 1s
527
+}
528
+
529
+.ripple:active:after {
530
+ transform: scale(0, 0);
531
+ opacity: .2;
532
+ transition: 0s
533
+}
web/gui/main.js
new
+4293
@@ -0,0 +1,4293 @@
1
+// Main JavaScript file for the Netdata GUI.
2
+
3
+// netdata snapshot data
4
+var netdataSnapshotData = null;
5
+
6
+// enable alarms checking and notifications
7
+var netdataShowAlarms = true;
8
+
9
+// enable registry updates
10
+var netdataRegistry = true;
11
+
12
+// forward definition only - not used here
13
+var netdataServer = undefined;
14
+var netdataServerStatic = undefined;
15
+var netdataCheckXSS = undefined;
16
+
17
+// control the welcome modal and analytics
18
+var this_is_demo = null;
19
+
20
+function escapeUserInputHTML(s) {
21
+ return s.toString()
22
+ .replace(/&/g, '&')
23
+ .replace(/</g, '<')
24
+ .replace(/>/g, '>')
25
+ .replace(/"/g, '"')
26
+ .replace(/#/g, '#')
27
+ .replace(/'/g, ''')
28
+ .replace(/\(/g, '(')
29
+ .replace(/\)/g, ')')
30
+ .replace(/\//g, '/');
31
+}
32
+
33
+function verifyURL(s) {
34
+ if (typeof (s) === 'string' && (s.startsWith('http://') || s.startsWith('https://'))) {
35
+ return s
36
+ .replace(/'/g, '%22')
37
+ .replace(/"/g, '%27')
38
+ .replace(/\)/g, '%28')
39
+ .replace(/\(/g, '%29');
40
+ }
41
+
42
+ console.log('invalid URL detected:');
43
+ console.log(s);
44
+ return 'javascript:alert("invalid url");';
45
+}
46
+
47
+// --------------------------------------------------------------------
48
+// urlOptions
49
+
50
+var urlOptions = {
51
+ hash: '#',
52
+ theme: null,
53
+ help: null,
54
+ mode: 'live', // 'live', 'print'
55
+ update_always: false,
56
+ pan_and_zoom: false,
57
+ server: null,
58
+ after: 0,
59
+ before: 0,
60
+ highlight: false,
61
+ highlight_after: 0,
62
+ highlight_before: 0,
63
+ nowelcome: false,
64
+ show_alarms: false,
65
+ chart: null,
66
+ family: null,
67
+ alarm: null,
68
+ alarm_unique_id: 0,
69
+ alarm_id: 0,
70
+ alarm_event_id: 0,
71
+
72
+ hasProperty: function (property) {
73
+ // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
74
+ return typeof this[property] !== 'undefined';
75
+ },
76
+
77
+ genHash: function (forReload) {
78
+ var hash = urlOptions.hash;
79
+
80
+ if (urlOptions.pan_and_zoom === true) {
81
+ hash += ';after=' + urlOptions.after.toString() +
82
+ ';before=' + urlOptions.before.toString();
83
+ }
84
+
85
+ if (urlOptions.highlight === true) {
86
+ hash += ';highlight_after=' + urlOptions.highlight_after.toString() +
87
+ ';highlight_before=' + urlOptions.highlight_before.toString();
88
+ }
89
+
90
+ if (urlOptions.theme !== null) {
91
+ hash += ';theme=' + urlOptions.theme.toString();
92
+ }
93
+
94
+ if (urlOptions.help !== null) {
95
+ hash += ';help=' + urlOptions.help.toString();
96
+ }
97
+
98
+ if (urlOptions.update_always === true) {
99
+ hash += ';update_always=true';
100
+ }
101
+
102
+ if (forReload === true && urlOptions.server !== null) {
103
+ hash += ';server=' + urlOptions.server.toString();
104
+ }
105
+
106
+ if (urlOptions.mode !== 'live') {
107
+ hash += ';mode=' + urlOptions.mode;
108
+ }
109
+
110
+ return hash;
111
+ },
112
+
113
+ parseHash: function () {
114
+ var variables = document.location.hash.split(';');
115
+ var len = variables.length;
116
+ while (len--) {
117
+ if (len !== 0) {
118
+ var p = variables[len].split('=');
119
+ if (urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined') {
120
+ urlOptions[p[0]] = decodeURIComponent(p[1]);
121
+ }
122
+ }
123
+ else {
124
+ if (variables[len].length > 0) {
125
+ urlOptions.hash = variables[len];
126
+ }
127
+ }
128
+ }
129
+
130
+ var booleans = ['nowelcome', 'show_alarms', 'update_always'];
131
+ len = booleans.length;
132
+ while (len--) {
133
+ if (urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1) {
134
+ urlOptions[booleans[len]] = true;
135
+ } else {
136
+ urlOptions[booleans[len]] = false;
137
+ }
138
+ }
139
+
140
+ var numeric = ['after', 'before', 'highlight_after', 'highlight_before'];
141
+ len = numeric.length;
142
+ while (len--) {
143
+ if (typeof urlOptions[numeric[len]] === 'string') {
144
+ try {
145
+ urlOptions[numeric[len]] = parseInt(urlOptions[numeric[len]]);
146
+ }
147
+ catch (e) {
148
+ console.log('failed to parse URL hash parameter ' + numeric[len]);
149
+ urlOptions[numeric[len]] = 0;
150
+ }
151
+ }
152
+ }
153
+
154
+ if (urlOptions.server !== null && urlOptions.server !== '') {
155
+ netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString();
156
+ netdataServer = urlOptions.server;
157
+ netdataCheckXSS = true;
158
+ }
159
+ else {
160
+ urlOptions.server = null;
161
+ }
162
+
163
+ if (urlOptions.before > 0 && urlOptions.after > 0) {
164
+ urlOptions.pan_and_zoom = true;
165
+ urlOptions.nowelcome = true;
166
+ }
167
+ else {
168
+ urlOptions.pan_and_zoom = false;
169
+ }
170
+
171
+ if (urlOptions.highlight_before > 0 && urlOptions.highlight_after > 0) {
172
+ urlOptions.highlight = true;
173
+ }
174
+ else {
175
+ urlOptions.highlight = false;
176
+ }
177
+
178
+ switch (urlOptions.mode) {
179
+ case 'print':
180
+ urlOptions.theme = 'white';
181
+ urlOptions.welcome = false;
182
+ urlOptions.help = false;
183
+ urlOptions.show_alarms = false;
184
+
185
+ if (urlOptions.pan_and_zoom === false) {
186
+ urlOptions.pan_and_zoom = true;
187
+ urlOptions.before = Date.now();
188
+ urlOptions.after = urlOptions.before - 600000;
189
+ }
190
+
191
+ netdataShowAlarms = false;
192
+ netdataRegistry = false;
193
+ this_is_demo = false;
194
+ break;
195
+
196
+ case 'live':
197
+ default:
198
+ urlOptions.mode = 'live';
199
+ break;
200
+ }
201
+
202
+ // console.log(urlOptions);
203
+ },
204
+
205
+ hashUpdate: function () {
206
+ history.replaceState(null, '', urlOptions.genHash(true));
207
+ },
208
+
209
+ netdataPanAndZoomCallback: function (status, after, before) {
210
+ //console.log(1);
211
+ //console.log(new Error().stack);
212
+
213
+ if (netdataSnapshotData === null) {
214
+ urlOptions.pan_and_zoom = status;
215
+ urlOptions.after = after;
216
+ urlOptions.before = before;
217
+ urlOptions.hashUpdate();
218
+ }
219
+ },
220
+
221
+ netdataHighlightCallback: function (status, after, before) {
222
+ //console.log(2);
223
+ //console.log(new Error().stack);
224
+
225
+ if (status === true && (after === null || before === null || after <= 0 || before <= 0 || after >= before)) {
226
+ status = false;
227
+ after = 0;
228
+ before = 0;
229
+ }
230
+
231
+ if (netdataSnapshotData === null) {
232
+ urlOptions.highlight = status;
233
+ } else {
234
+ urlOptions.highlight = false;
235
+ }
236
+
237
+ urlOptions.highlight_after = Math.round(after);
238
+ urlOptions.highlight_before = Math.round(before);
239
+ urlOptions.hashUpdate();
240
+
241
+ var show_eye = NETDATA.globalChartUnderlay.hasViewport();
242
+
243
+ if (status === true && after > 0 && before > 0 && after < before) {
244
+ var d1 = NETDATA.dateTime.localeDateString(after);
245
+ var d2 = NETDATA.dateTime.localeDateString(before);
246
+ if (d1 === d2) {
247
+ d2 = '';
248
+ }
249
+ document.getElementById('navbar-highlight-content').innerHTML =
250
+ ((show_eye === true) ? '<span class="navbar-highlight-bar highlight-tooltip" onclick="urlOptions.showHighlight();" title="restore the highlighted view" data-toggle="tooltip" data-placement="bottom">' : '<span>').toString()
251
+ + 'highlighted time-frame'
252
+ + ' <b>' + d1 + ' <code>' + NETDATA.dateTime.localeTimeString(after) + '</code></b> to '
253
+ + ' <b>' + d2 + ' <code>' + NETDATA.dateTime.localeTimeString(before) + '</code></b>, '
254
+ + 'duration <b>' + NETDATA.seconds4human(Math.round((before - after) / 1000)) + '</b>'
255
+ + '</span>'
256
+ + '<span class="navbar-highlight-button-right highlight-tooltip" onclick="urlOptions.clearHighlight();" title="clear the highlighted time-frame" data-toggle="tooltip" data-placement="bottom"><i class="fas fa-times"></i></span>';
257
+
258
+ $('.navbar-highlight').show();
259
+
260
+ $('.highlight-tooltip').tooltip({
261
+ html: true,
262
+ delay: {show: 500, hide: 0},
263
+ container: 'body'
264
+ });
265
+ }
266
+ else {
267
+ $('.navbar-highlight').hide();
268
+ }
269
+ },
270
+
271
+ clearHighlight: function () {
272
+ NETDATA.globalChartUnderlay.clear();
273
+
274
+ if (NETDATA.globalPanAndZoom.isActive() === true) {
275
+ NETDATA.globalPanAndZoom.clearMaster();
276
+ }
277
+ },
278
+
279
+ showHighlight: function () {
280
+ NETDATA.globalChartUnderlay.focus();
281
+ }
282
+};
283
+
284
+urlOptions.parseHash();
285
+
286
+// --------------------------------------------------------------------
287
+// check options that should be processed before loading netdata.js
288
+
289
+var localStorageTested = -1;
290
+
291
+function localStorageTest() {
292
+ if (localStorageTested !== -1) {
293
+ return localStorageTested;
294
+ }
295
+
296
+ if (typeof Storage !== "undefined" && typeof localStorage === 'object') {
297
+ var test = 'test';
298
+ try {
299
+ localStorage.setItem(test, test);
300
+ localStorage.removeItem(test);
301
+ localStorageTested = true;
302
+ }
303
+ catch (e) {
304
+ console.log(e);
305
+ localStorageTested = false;
306
+ }
307
+ }
308
+ else {
309
+ localStorageTested = false;
310
+ }
311
+
312
+ return localStorageTested;
313
+}
314
+
315
+function loadLocalStorage(name) {
316
+ var ret = null;
317
+
318
+ try {
319
+ if (localStorageTest() === true) {
320
+ ret = localStorage.getItem(name);
321
+ } else {
322
+ console.log('localStorage is not available');
323
+ }
324
+ }
325
+ catch (error) {
326
+ console.log(error);
327
+ return null;
328
+ }
329
+
330
+ if (typeof ret === 'undefined' || ret === null) {
331
+ return null;
332
+ }
333
+
334
+ // console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
335
+
336
+ return ret;
337
+}
338
+
339
+function saveLocalStorage(name, value) {
340
+ // console.log('saving: ' + name.toString() + ' = ' + value.toString());
341
+ try {
342
+ if (localStorageTest() === true) {
343
+ localStorage.setItem(name, value.toString());
344
+ return true;
345
+ }
346
+ }
347
+ catch (error) {
348
+ console.log(error);
349
+ }
350
+
351
+ return false;
352
+}
353
+
354
+function getTheme(def) {
355
+ if (urlOptions.mode === 'print') {
356
+ return 'white';
357
+ }
358
+
359
+ var ret = loadLocalStorage('netdataTheme');
360
+ if (typeof ret === 'undefined' || ret === null || ret === 'undefined') {
361
+ return def;
362
+ } else {
363
+ return ret;
364
+ }
365
+}
366
+
367
+function setTheme(theme) {
368
+ if (urlOptions.mode === 'print') {
369
+ return false;
370
+ }
371
+
372
+ if (theme === netdataTheme) {
373
+ return false;
374
+ }
375
+ return saveLocalStorage('netdataTheme', theme);
376
+}
377
+
378
+var netdataTheme = getTheme('slate');
379
+var netdataShowHelp = true;
380
+
381
+if (urlOptions.theme !== null) {
382
+ setTheme(urlOptions.theme);
383
+ netdataTheme = urlOptions.theme;
384
+}
385
+else {
386
+ urlOptions.theme = netdataTheme;
387
+}
388
+
389
+if (urlOptions.help !== null) {
390
+ saveLocalStorage('options.show_help', urlOptions.help);
391
+ netdataShowHelp = urlOptions.help;
392
+}
393
+else {
394
+ urlOptions.help = loadLocalStorage('options.show_help');
395
+}
396
+
397
+// --------------------------------------------------------------------
398
+// natural sorting
399
+// http://www.davekoelle.com/files/alphanum.js - LGPL
400
+
401
+function naturalSortChunkify(t) {
402
+ var tz = [];
403
+ var x = 0, y = -1, n = 0, i, j;
404
+
405
+ while (i = (j = t.charAt(x++)).charCodeAt(0)) {
406
+ var m = (i >= 48 && i <= 57);
407
+ if (m !== n) {
408
+ tz[++y] = "";
409
+ n = m;
410
+ }
411
+ tz[y] += j;
412
+ }
413
+
414
+ return tz;
415
+}
416
+
417
+function naturalSortCompare(a, b) {
418
+ var aa = naturalSortChunkify(a.toLowerCase());
419
+ var bb = naturalSortChunkify(b.toLowerCase());
420
+
421
+ for (var x = 0; aa[x] && bb[x]; x++) {
422
+ if (aa[x] !== bb[x]) {
423
+ var c = Number(aa[x]), d = Number(bb[x]);
424
+ if (c.toString() === aa[x] && d.toString() === bb[x]) {
425
+ return c - d;
426
+ } else {
427
+ return (aa[x] > bb[x]) ? 1 : -1;
428
+ }
429
+ }
430
+ }
431
+
432
+ return aa.length - bb.length;
433
+}
434
+
435
+// --------------------------------------------------------------------
436
+// saving files to client
437
+
438
+function saveTextToClient(data, filename) {
439
+ var blob = new Blob([data], {
440
+ type: 'application/octet-stream'
441
+ });
442
+
443
+ var url = URL.createObjectURL(blob);
444
+ var link = document.createElement('a');
445
+ link.setAttribute('href', url);
446
+ link.setAttribute('download', filename);
447
+
448
+ var el = document.getElementById('hiddenDownloadLinks');
449
+ el.innerHTML = '';
450
+ el.appendChild(link);
451
+
452
+ setTimeout(function () {
453
+ el.removeChild(link);
454
+ URL.revokeObjectURL(url);
455
+ }, 60);
456
+
457
+ link.click();
458
+}
459
+
460
+function saveObjectToClient(data, filename) {
461
+ saveTextToClient(JSON.stringify(data), filename);
462
+}
463
+
464
+// --------------------------------------------------------------------
465
+// registry call back to render my-netdata menu
466
+
467
+var netdataRegistryCallback = function (machines_array) {
468
+ var el = '';
469
+ var a1 = '';
470
+ var found = 0, hosted = 0;
471
+ var len, i, url, hostname, icon;
472
+
473
+ if (options.hosts.length > 1) {
474
+ // there are mirrored hosts here
475
+
476
+ el += '<li><a href="#" onClick="return false;" style="color: #666;" target="_blank">databases available on this host</a></li>';
477
+ a1 += '<li><a href="#" onClick="return false;"><i class="fas fa-info-circle" style="color: #666;"></i></a></li>';
478
+
479
+ var base = document.location.origin.toString() + document.location.pathname.toString();
480
+ if (base.endsWith("/host/" + options.hostname + "/")) {
481
+ base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length);
482
+ }
483
+
484
+ if (base.endsWith("/")) {
485
+ base = base.substring(0, base.length - 1);
486
+ }
487
+
488
+ var master = options.hosts[0].hostname;
489
+ var sorted = options.hosts.sort(function (a, b) {
490
+ if (a.hostname === master) {
491
+ return -1;
492
+ }
493
+ return naturalSortCompare(a.hostname, b.hostname);
494
+ });
495
+
496
+ i = 0;
497
+ len = sorted.length;
498
+ while (len--) {
499
+ hostname = sorted[i].hostname;
500
+ if (hostname === master) {
501
+ url = base + "/";
502
+ icon = "home";
503
+ }
504
+ else {
505
+ url = base + "/host/" + hostname + "/";
506
+ icon = "window-restore";
507
+ }
508
+
509
+ el += '<li id="registry_server_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '#" onClick="return gotoHostedModalHandler(\'' + url + '\');">' + hostname + '</a></li>';
510
+ a1 += '<li id="registry_action_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '#" onClick="return gotoHostedModalHandler(\'' + url + '\');"><i class="fas fa-' + icon + '" style="color: #999;"></i></a></li>';
511
+ hosted++;
512
+ i++;
513
+ }
514
+
515
+ el += '<li role="separator" class="divider"></li>';
516
+ a1 += '<li role="separator" class="divider"></li>';
517
+ }
518
+
519
+ if (machines_array === null) {
520
+ var ret = loadLocalStorage("registryCallback");
521
+ if (typeof ret !== 'undefined' && ret !== null) {
522
+ machines_array = JSON.parse(ret);
523
+ console.log("failed to contact the registry - loaded registry data from browser local storage");
524
+ }
525
+ }
526
+
527
+ if (machines_array) {
528
+ saveLocalStorage("registryCallback", JSON.stringify(machines_array));
529
+
530
+ var machines = machines_array.sort(function (a, b) {
531
+ return naturalSortCompare(a.name, b.name);
532
+ });
533
+
534
+ i = 0;
535
+ len = machines.length;
536
+ while (len--) {
537
+ var u = machines[i++];
538
+ found++;
539
+ el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '#" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>';
540
+ a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fas fa-trash" style="color: #999;"></i></a></li>';
541
+ }
542
+ }
543
+
544
+ if (!found) {
545
+ if (machines) {
546
+ el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
547
+ } else {
548
+ el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
549
+ }
550
+
551
+ a1 += '<li><a href="#" onClick="return false;"> </a></li>';
552
+
553
+ el += '<li role="separator" class="divider"></li>' +
554
+ '<li><a href="//london.netdata.rocks/default.html">UK - London (DigitalOcean.com)</a></li>' +
555
+ '<li><a href="//newyork.netdata.rocks/default.html">US - New York (DigitalOcean.com)</a></li>' +
556
+ '<li><a href="//sanfrancisco.netdata.rocks/default.html">US - San Francisco (DigitalOcean.com)</a></li>' +
557
+ '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
558
+ '<li><a href="//frankfurt.netdata.rocks/default.html">Germany - Frankfurt (DigitalOcean.com)</a></li>' +
559
+ '<li><a href="//toronto.netdata.rocks/default.html">Canada - Toronto (DigitalOcean.com)</a></li>' +
560
+ '<li><a href="//singapore.netdata.rocks/default.html">Japan - Singapore (DigitalOcean.com)</a></li>' +
561
+ '<li><a href="//bangalore.netdata.rocks/default.html">India - Bangalore (DigitalOcean.com)</a></li>';
562
+ a1 += '<li role="separator" class="divider"></li>' +
563
+ '<li><a href="#"> </a></li>' +
564
+ '<li><a href="#"> </a></li>' +
565
+ '<li><a href="#"> </a></li>' +
566
+ '<li><a href="#"> </a></li>' +
567
+ '<li><a href="#"> </a></li>' +
568
+ '<li><a href="#"> </a></li>' +
569
+ '<li><a href="#"> </a></li>' +
570
+ '<li><a href="#"> </a></li>';
571
+ }
572
+
573
+ el += '<li role="separator" class="divider"></li>';
574
+ a1 += '<li role="separator" class="divider"></li>';
575
+
576
+ el += '<li><a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" style="color: #999;" target="_blank">What is this?</a></li>';
577
+ a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fas fa-cog" style="color: #999;"></i></a></li>';
578
+
579
+ document.getElementById('mynetdata_servers').innerHTML = el;
580
+ document.getElementById('mynetdata_servers2').innerHTML = el;
581
+ document.getElementById('mynetdata_actions1').innerHTML = a1;
582
+
583
+ gotoServerInit();
584
+};
585
+
586
+function isdemo() {
587
+ if (this_is_demo !== null) {
588
+ return this_is_demo;
589
+ }
590
+ this_is_demo = false;
591
+
592
+ try {
593
+ if (typeof document.location.hostname === 'string') {
594
+ if (document.location.hostname.endsWith('.my-netdata.io') ||
595
+ document.location.hostname.endsWith('.mynetdata.io') ||
596
+ document.location.hostname.endsWith('.netdata.rocks') ||
597
+ document.location.hostname.endsWith('.netdata.ai') ||
598
+ document.location.hostname.endsWith('.netdata.live') ||
599
+ document.location.hostname.endsWith('.firehol.org') ||
600
+ document.location.hostname.endsWith('.netdata.online') ||
601
+ document.location.hostname.endsWith('.netdata.cloud')) {
602
+ this_is_demo = true;
603
+ }
604
+ }
605
+ }
606
+ catch (error) {
607
+ }
608
+ return this_is_demo;
609
+}
610
+
611
+function netdataURL(url, forReload) {
612
+ if (typeof url === 'undefined')
613
+ // url = document.location.toString();
614
+ {
615
+ url = '';
616
+ }
617
+
618
+ if (url.indexOf('#') !== -1) {
619
+ url = url.substring(0, url.indexOf('#'));
620
+ }
621
+
622
+ var hash = urlOptions.genHash(forReload);
623
+
624
+ // console.log('netdataURL: ' + url + hash);
625
+
626
+ return url + hash;
627
+}
628
+
629
+function netdataReload(url) {
630
+ document.location = verifyURL(netdataURL(url, true));
631
+
632
+ // since we play with hash
633
+ // this is needed to reload the page
634
+ location.reload();
635
+}
636
+
637
+function gotoHostedModalHandler(url) {
638
+ document.location = verifyURL(url + urlOptions.genHash());
639
+ return false;
640
+}
641
+
642
+var gotoServerValidateRemaining = 0;
643
+var gotoServerMiddleClick = false;
644
+var gotoServerStop = false;
645
+
646
+function gotoServerValidateUrl(id, guid, url) {
647
+ var penalty = 0;
648
+ var error = 'failed';
649
+
650
+ if (document.location.toString().startsWith('http://') && url.toString().startsWith('https://'))
651
+ // we penalize https only if the current url is http
652
+ // to allow the user walk through all its servers.
653
+ {
654
+ penalty = 500;
655
+ } else if (document.location.toString().startsWith('https://') && url.toString().startsWith('http://')) {
656
+ error = 'can\'t check';
657
+ }
658
+
659
+ var finalURL = netdataURL(url);
660
+
661
+ setTimeout(function () {
662
+ document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + verifyURL(finalURL) + '" target="_blank">' + escapeUserInputHTML(url) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
663
+
664
+ NETDATA.registry.hello(url, function (data) {
665
+ if (typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid === guid) {
666
+ // console.log('OK ' + id + ' URL: ' + url);
667
+ document.getElementById(guid + '-' + id + '-status').innerHTML = "OK";
668
+
669
+ if (!gotoServerStop) {
670
+ gotoServerStop = true;
671
+
672
+ if (gotoServerMiddleClick) {
673
+ window.open(verifyURL(finalURL), '_blank');
674
+ gotoServerMiddleClick = false;
675
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + verifyURL(finalURL) + '">' + escapeUserInputHTML(url) + '</a></b><br/>(check your pop-up blocker if it fails)';
676
+ }
677
+ else {
678
+ document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML(url) + '</small>';
679
+ document.location = verifyURL(finalURL);
680
+ }
681
+ }
682
+ }
683
+ else {
684
+ if (typeof data !== 'undefined' && data !== null && typeof data.machine_guid === 'string' && data.machine_guid !== guid) {
685
+ error = 'wrong machine';
686
+ }
687
+
688
+ document.getElementById(guid + '-' + id + '-status').innerHTML = error;
689
+ gotoServerValidateRemaining--;
690
+ if (gotoServerValidateRemaining <= 0) {
691
+ gotoServerMiddleClick = false;
692
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>';
693
+ }
694
+ }
695
+ });
696
+ }, (id * 50) + penalty);
697
+}
698
+
699
+function gotoServerModalHandler(guid) {
700
+ // console.log('goto server: ' + guid);
701
+
702
+ gotoServerStop = false;
703
+ var checked = {};
704
+ var len = NETDATA.registry.machines[guid].alternate_urls.length;
705
+ var count = 0;
706
+
707
+ document.getElementById('gotoServerResponse').innerHTML = '';
708
+ document.getElementById('gotoServerList').innerHTML = '';
709
+ document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name;
710
+ $('#gotoServerModal').modal('show');
711
+
712
+ gotoServerValidateRemaining = len;
713
+ while (len--) {
714
+ var url = NETDATA.registry.machines[guid].alternate_urls[len];
715
+ checked[url] = true;
716
+ gotoServerValidateUrl(count++, guid, url);
717
+ }
718
+
719
+ setTimeout(function () {
720
+ if (gotoServerStop === false) {
721
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>';
722
+ NETDATA.registry.search(guid, function (data) {
723
+ // console.log(data);
724
+ len = data.urls.length;
725
+ while (len--) {
726
+ var url = data.urls[len][1];
727
+ // console.log(url);
728
+ if (typeof checked[url] === 'undefined') {
729
+ gotoServerValidateRemaining++;
730
+ checked[url] = true;
731
+ gotoServerValidateUrl(count++, guid, url);
732
+ }
733
+ }
734
+ });
735
+ }
736
+ }, 2000);
737
+ return false;
738
+}
739
+
740
+function gotoServerInit() {
741
+ $(".registry_link").on('click', function (e) {
742
+ if (e.which === 2) {
743
+ e.preventDefault();
744
+ gotoServerMiddleClick = true;
745
+ }
746
+ else {
747
+ gotoServerMiddleClick = false;
748
+ }
749
+
750
+ return true;
751
+ });
752
+}
753
+
754
+function switchRegistryModalHandler() {
755
+ document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid;
756
+ document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server;
757
+ document.getElementById('switchRegistryResponse').innerHTML = '';
758
+ $('#switchRegistryModal').modal('show');
759
+}
760
+
761
+function notifyForSwitchRegistry() {
762
+ var n = document.getElementById('switchRegistryPersonGUID').value;
763
+
764
+ if (n !== '' && n.length === 36) {
765
+ NETDATA.registry.switch(n, function (result) {
766
+ if (result !== null) {
767
+ $('#switchRegistryModal').modal('hide');
768
+ NETDATA.registry.init();
769
+ }
770
+ else {
771
+ document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>";
772
+ }
773
+ });
774
+ }
775
+ else {
776
+ document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>";
777
+ }
778
+}
779
+
780
+var deleteRegistryUrl = null;
781
+
782
+function deleteRegistryModalHandler(guid, name, url) {
783
+ void (guid);
784
+
785
+ deleteRegistryUrl = url;
786
+ document.getElementById('deleteRegistryServerName').innerHTML = name;
787
+ document.getElementById('deleteRegistryServerName2').innerHTML = name;
788
+ document.getElementById('deleteRegistryServerURL').innerHTML = url;
789
+ document.getElementById('deleteRegistryResponse').innerHTML = '';
790
+ $('#deleteRegistryModal').modal('show');
791
+}
792
+
793
+function notifyForDeleteRegistry() {
794
+ if (deleteRegistryUrl) {
795
+ NETDATA.registry.delete(deleteRegistryUrl, function (result) {
796
+ if (result !== null) {
797
+ deleteRegistryUrl = null;
798
+ $('#deleteRegistryModal').modal('hide');
799
+ NETDATA.registry.init();
800
+ }
801
+ else {
802
+ document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>";
803
+ }
804
+ });
805
+ }
806
+}
807
+
808
+var options = {
809
+ menus: {},
810
+ submenu_names: {},
811
+ data: null,
812
+ hostname: 'netdata_server', // will be overwritten by the netdata server
813
+ version: 'unknown',
814
+ hosts: [],
815
+
816
+ duration: 0, // the default duration of the charts
817
+ update_every: 1,
818
+
819
+ chartsPerRow: 0,
820
+ // chartsMinWidth: 1450,
821
+ chartsHeight: 180,
822
+};
823
+
824
+function chartsPerRow(total) {
825
+ void (total);
826
+
827
+ if (options.chartsPerRow === 0) {
828
+ return 1;
829
+ //var width = Math.floor(total / options.chartsMinWidth);
830
+ //if(width === 0) width = 1;
831
+ //return width;
832
+ }
833
+ else {
834
+ return options.chartsPerRow;
835
+ }
836
+}
837
+
838
+function prioritySort(a, b) {
839
+ if (a.priority < b.priority) {
840
+ return -1;
841
+ }
842
+ if (a.priority > b.priority) {
843
+ return 1;
844
+ }
845
+ return naturalSortCompare(a.name, b.name);
846
+}
847
+
848
+function sortObjectByPriority(object) {
849
+ var idx = {};
850
+ var sorted = [];
851
+
852
+ for (var i in object) {
853
+ if (!object.hasOwnProperty(i)) {
854
+ continue;
855
+ }
856
+
857
+ if (typeof idx[i] === 'undefined') {
858
+ idx[i] = object[i];
859
+ sorted.push(i);
860
+ }
861
+ }
862
+
863
+ sorted.sort(function (a, b) {
864
+ if (idx[a].priority < idx[b].priority) {
865
+ return -1;
866
+ }
867
+ if (idx[a].priority > idx[b].priority) {
868
+ return 1;
869
+ }
870
+ return naturalSortCompare(a, b);
871
+ });
872
+
873
+ return sorted;
874
+}
875
+
876
+// ----------------------------------------------------------------------------
877
+// scroll to a section, without changing the browser history
878
+
879
+function scrollToId(hash) {
880
+ if (hash && hash !== '' && document.getElementById(hash) !== null) {
881
+ var offset = $('#' + hash).offset();
882
+ if (typeof offset !== 'undefined') {
883
+ //console.log('scrolling to ' + hash + ' at ' + offset.top.toString());
884
+ $('html, body').animate({scrollTop: offset.top - 30}, 0);
885
+ }
886
+ }
887
+
888
+ // we must return false to prevent the default action
889
+ return false;
890
+}
891
+
892
+// ----------------------------------------------------------------------------
893
+
894
+// user editable information
895
+var customDashboard = {
896
+ menu: {},
897
+ submenu: {},
898
+ context: {}
899
+};
900
+
901
+// netdata standard information
902
+var netdataDashboard = {
903
+ sparklines_registry: {},
904
+ os: 'unknown',
905
+
906
+ menu: {},
907
+ submenu: {},
908
+ context: {},
909
+
910
+ // generate a sparkline
911
+ // used in the documentation
912
+ sparkline: function (prefix, chart, dimension, units, suffix) {
913
+ if (options.data === null || typeof options.data.charts === 'undefined') {
914
+ return '';
915
+ }
916
+
917
+ if (typeof options.data.charts[chart] === 'undefined') {
918
+ return '';
919
+ }
920
+
921
+ if (typeof options.data.charts[chart].dimensions === 'undefined') {
922
+ return '';
923
+ }
924
+
925
+ if (typeof options.data.charts[chart].dimensions[dimension] === 'undefined') {
926
+ return '';
927
+ }
928
+
929
+ var key = chart + '.' + dimension;
930
+
931
+ if (typeof units === 'undefined') {
932
+ units = '';
933
+ }
934
+
935
+ if (typeof this.sparklines_registry[key] === 'undefined') {
936
+ this.sparklines_registry[key] = {count: 1};
937
+ } else {
938
+ this.sparklines_registry[key].count++;
939
+ }
940
+
941
+ key = key + '.' + this.sparklines_registry[key].count;
942
+
943
+ return prefix + '<div class="netdata-container" data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')' + suffix;
944
+ },
945
+
946
+ gaugeChart: function (title, width, dimensions, colors) {
947
+ if (typeof colors === 'undefined') {
948
+ colors = '';
949
+ }
950
+
951
+ if (typeof dimensions === 'undefined') {
952
+ dimensions = '';
953
+ }
954
+
955
+ return '<div class="netdata-container" data-netdata="CHART_UNIQUE_ID"'
956
+ + ' data-dimensions="' + dimensions + '"'
957
+ + ' data-chart-library="gauge"'
958
+ + ' data-gauge-adjust="width"'
959
+ + ' data-title="' + title + '"'
960
+ + ' data-width="' + width + '"'
961
+ + ' data-before="0"'
962
+ + ' data-after="-CHART_DURATION"'
963
+ + ' data-points="CHART_DURATION"'
964
+ + ' data-colors="' + colors + '"'
965
+ + ' role="application"></div>';
966
+ },
967
+
968
+ anyAttribute: function (obj, attr, key, def) {
969
+ if (typeof (obj[key]) !== 'undefined') {
970
+ var x = obj[key][attr];
971
+
972
+ if (typeof (x) === 'undefined') {
973
+ return def;
974
+ }
975
+
976
+ if (typeof (x) === 'function') {
977
+ return x(netdataDashboard.os);
978
+ }
979
+
980
+ return x;
981
+ }
982
+
983
+ return def;
984
+ },
985
+
986
+ menuTitle: function (chart) {
987
+ if (typeof chart.menu_pattern !== 'undefined') {
988
+ return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString()
989
+ + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' ');
990
+ }
991
+
992
+ return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' ');
993
+ },
994
+
995
+ menuIcon: function (chart) {
996
+ if (typeof chart.menu_pattern !== 'undefined') {
997
+ return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '<i class="fas fa-puzzle-piece"></i>').toString();
998
+ }
999
+
1000
+ return this.anyAttribute(this.menu, 'icon', chart.menu, '<i class="fas fa-puzzle-piece"></i>');
1001
+ },
1002
+
1003
+ menuInfo: function (chart) {
1004
+ if (typeof chart.menu_pattern !== 'undefined') {
1005
+ return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null);
1006
+ }
1007
+
1008
+ return this.anyAttribute(this.menu, 'info', chart.menu, null);
1009
+ },
1010
+
1011
+ menuHeight: function (chart) {
1012
+ if (typeof chart.menu_pattern !== 'undefined') {
1013
+ return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0);
1014
+ }
1015
+
1016
+ return this.anyAttribute(this.menu, 'height', chart.menu, 1.0);
1017
+ },
1018
+
1019
+ submenuTitle: function (menu, submenu) {
1020
+ var key = menu + '.' + submenu;
1021
+ // console.log(key);
1022
+ var title = this.anyAttribute(this.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');
1023
+ if (title.length > 28) {
1024
+ var a = title.substring(0, 13);
1025
+ var b = title.substring(title.length - 12, title.length);
1026
+ return a + '...' + b;
1027
+ }
1028
+ return title;
1029
+ },
1030
+
1031
+ submenuInfo: function (menu, submenu) {
1032
+ var key = menu + '.' + submenu;
1033
+ return this.anyAttribute(this.submenu, 'info', key, null);
1034
+ },
1035
+
1036
+ submenuHeight: function (menu, submenu, relative) {
1037
+ var key = menu + '.' + submenu;
1038
+ return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative;
1039
+ },
1040
+
1041
+ contextInfo: function (id) {
1042
+ var x = this.anyAttribute(this.context, 'info', id, null);
1043
+
1044
+ if (x !== null) {
1045
+ return '<div class="shorten dashboard-context-info netdata-chart-alignment" role="document">' + x + '</div>';
1046
+ } else {
1047
+ return '';
1048
+ }
1049
+ },
1050
+
1051
+ contextValueRange: function (id) {
1052
+ if (typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined') {
1053
+ return this.context[id].valueRange;
1054
+ } else {
1055
+ return '[null, null]';
1056
+ }
1057
+ },
1058
+
1059
+ contextHeight: function (id, def) {
1060
+ if (typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined') {
1061
+ return def * this.context[id].height;
1062
+ } else {
1063
+ return def;
1064
+ }
1065
+ },
1066
+
1067
+ contextDecimalDigits: function (id, def) {
1068
+ if (typeof this.context[id] !== 'undefined' && typeof this.context[id].decimalDigits !== 'undefined') {
1069
+ return this.context[id].decimalDigits;
1070
+ } else {
1071
+ return def;
1072
+ }
1073
+ }
1074
+};
1075
+
1076
+// ----------------------------------------------------------------------------
1077
+
1078
+// enrich the data structure returned by netdata
1079
+// to reflect our menu system and content
1080
+// TODO: this is a shame - we should fix charts naming (issue #807)
1081
+function enrichChartData(chart) {
1082
+ var parts = chart.type.split('_');
1083
+ var tmp = parts[0];
1084
+
1085
+ switch (tmp) {
1086
+ case 'ap':
1087
+ case 'net':
1088
+ case 'disk':
1089
+ case 'statsd':
1090
+ chart.menu = tmp;
1091
+ break;
1092
+
1093
+ case 'apache':
1094
+ chart.menu = chart.type;
1095
+ if (parts.length > 2 && parts[1] === 'cache') {
1096
+ chart.menu_pattern = tmp + '_' + parts[1];
1097
+ } else if (parts.length > 1) {
1098
+ chart.menu_pattern = tmp;
1099
+ }
1100
+ break;
1101
+
1102
+ case 'bind':
1103
+ chart.menu = chart.type;
1104
+ if (parts.length > 2 && parts[1] === 'rndc') {
1105
+ chart.menu_pattern = tmp + '_' + parts[1];
1106
+ } else if (parts.length > 1) {
1107
+ chart.menu_pattern = tmp;
1108
+ }
1109
+ break;
1110
+
1111
+ case 'cgroup':
1112
+ chart.menu = chart.type;
1113
+ if (chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) {
1114
+ chart.menu_pattern = 'cgqemu';
1115
+ } else {
1116
+ chart.menu_pattern = 'cgroup';
1117
+ }
1118
+ break;
1119
+
1120
+ case 'go':
1121
+ chart.menu = chart.type;
1122
+ if (parts.length > 2 && parts[1] === 'expvar') {
1123
+ chart.menu_pattern = tmp + '_' + parts[1];
1124
+ } else if (parts.length > 1) {
1125
+ chart.menu_pattern = tmp;
1126
+ }
1127
+ break;
1128
+
1129
+ case 'isc':
1130
+ chart.menu = chart.type;
1131
+ if (parts.length > 2 && parts[1] === 'dhcpd') {
1132
+ chart.menu_pattern = tmp + '_' + parts[1];
1133
+ } else if (parts.length > 1) {
1134
+ chart.menu_pattern = tmp;
1135
+ }
1136
+ break;
1137
+
1138
+ case 'ovpn':
1139
+ chart.menu = chart.type;
1140
+ if (parts.length > 3 && parts[1] === 'status' && parts[2] === 'log') {
1141
+ chart.menu_pattern = tmp + '_' + parts[1];
1142
+ } else if (parts.length > 1) {
1143
+ chart.menu_pattern = tmp;
1144
+ }
1145
+ break;
1146
+
1147
+ case 'smartd':
1148
+ case 'web':
1149
+ chart.menu = chart.type;
1150
+ if (parts.length > 2 && parts[1] === 'log') {
1151
+ chart.menu_pattern = tmp + '_' + parts[1];
1152
+ } else if (parts.length > 1) {
1153
+ chart.menu_pattern = tmp;
1154
+ }
1155
+ break;
1156
+
1157
+ case 'tc':
1158
+ chart.menu = tmp;
1159
+
1160
+ // find a name for this device from fireqos info
1161
+ // we strip '_(in|out)' or '(in|out)_'
1162
+ if (chart.context === 'tc.qos' && (typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)) {
1163
+ var n = chart.name.split('.')[1];
1164
+ if (n.endsWith('_in')) {
1165
+ options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in'));
1166
+ } else if (n.endsWith('_out')) {
1167
+ options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out'));
1168
+ } else if (n.startsWith('in_')) {
1169
+ options.submenu_names[chart.family] = n.slice(3, n.length);
1170
+ } else if (n.startsWith('out_')) {
1171
+ options.submenu_names[chart.family] = n.slice(4, n.length);
1172
+ } else {
1173
+ options.submenu_names[chart.family] = n;
1174
+ }
1175
+ }
1176
+
1177
+ // increase the priority of IFB devices
1178
+ // to have inbound appear before outbound
1179
+ if (chart.id.match(/.*-ifb$/)) {
1180
+ chart.priority--;
1181
+ }
1182
+
1183
+ break;
1184
+
1185
+ default:
1186
+ chart.menu = chart.type;
1187
+ if (parts.length > 1) {
1188
+ chart.menu_pattern = tmp;
1189
+ }
1190
+ break;
1191
+ }
1192
+
1193
+ chart.submenu = chart.family;
1194
+}
1195
+
1196
+// ----------------------------------------------------------------------------
1197
+
1198
+function headMain(os, charts, duration) {
1199
+ void (os);
1200
+
1201
+ if (urlOptions.mode === 'print') {
1202
+ return '';
1203
+ }
1204
+
1205
+ var head = '';
1206
+
1207
+ if (typeof charts['system.swap'] !== 'undefined') {
1208
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.swap"'
1209
+ + ' data-dimensions="used"'
1210
+ + ' data-append-options="percentage"'
1211
+ + ' data-chart-library="easypiechart"'
1212
+ + ' data-title="Used Swap"'
1213
+ + ' data-units="%"'
1214
+ + ' data-easypiechart-max-value="100"'
1215
+ + ' data-width="9%"'
1216
+ + ' data-before="0"'
1217
+ + ' data-after="-' + duration.toString() + '"'
1218
+ + ' data-points="' + duration.toString() + '"'
1219
+ + ' data-colors="#DD4400"'
1220
+ + ' role="application"></div>';
1221
+ }
1222
+
1223
+ if (typeof charts['system.io'] !== 'undefined') {
1224
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
1225
+ + ' data-dimensions="in"'
1226
+ + ' data-chart-library="easypiechart"'
1227
+ + ' data-title="Disk Read"'
1228
+ + ' data-width="11%"'
1229
+ + ' data-before="0"'
1230
+ + ' data-after="-' + duration.toString() + '"'
1231
+ + ' data-points="' + duration.toString() + '"'
1232
+ + ' data-common-units="system.io.mainhead"'
1233
+ + ' role="application"></div>';
1234
+
1235
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
1236
+ + ' data-dimensions="out"'
1237
+ + ' data-chart-library="easypiechart"'
1238
+ + ' data-title="Disk Write"'
1239
+ + ' data-width="11%"'
1240
+ + ' data-before="0"'
1241
+ + ' data-after="-' + duration.toString() + '"'
1242
+ + ' data-points="' + duration.toString() + '"'
1243
+ + ' data-common-units="system.io.mainhead"'
1244
+ + ' role="application"></div>';
1245
+ }
1246
+ else if (typeof charts['system.pgpgio'] !== 'undefined') {
1247
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
1248
+ + ' data-dimensions="in"'
1249
+ + ' data-chart-library="easypiechart"'
1250
+ + ' data-title="Disk Read"'
1251
+ + ' data-width="11%"'
1252
+ + ' data-before="0"'
1253
+ + ' data-after="-' + duration.toString() + '"'
1254
+ + ' data-points="' + duration.toString() + '"'
1255
+ + ' data-common-units="system.pgpgio.mainhead"'
1256
+ + ' role="application"></div>';
1257
+
1258
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
1259
+ + ' data-dimensions="out"'
1260
+ + ' data-chart-library="easypiechart"'
1261
+ + ' data-title="Disk Write"'
1262
+ + ' data-width="11%"'
1263
+ + ' data-before="0"'
1264
+ + ' data-after="-' + duration.toString() + '"'
1265
+ + ' data-points="' + duration.toString() + '"'
1266
+ + ' data-common-units="system.pgpgio.mainhead"'
1267
+ + ' role="application"></div>';
1268
+ }
1269
+
1270
+ if (typeof charts['system.cpu'] !== 'undefined') {
1271
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.cpu"'
1272
+ + ' data-chart-library="gauge"'
1273
+ + ' data-title="CPU"'
1274
+ + ' data-units="%"'
1275
+ + ' data-gauge-max-value="100"'
1276
+ + ' data-width="20%"'
1277
+ + ' data-after="-' + duration.toString() + '"'
1278
+ + ' data-points="' + duration.toString() + '"'
1279
+ + ' data-colors="' + NETDATA.colors[12] + '"'
1280
+ + ' role="application"></div>';
1281
+ }
1282
+
1283
+ if (typeof charts['system.net'] !== 'undefined') {
1284
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
1285
+ + ' data-dimensions="received"'
1286
+ + ' data-chart-library="easypiechart"'
1287
+ + ' data-title="Net Inbound"'
1288
+ + ' data-width="11%"'
1289
+ + ' data-before="0"'
1290
+ + ' data-after="-' + duration.toString() + '"'
1291
+ + ' data-points="' + duration.toString() + '"'
1292
+ + ' data-common-units="system.net.mainhead"'
1293
+ + ' role="application"></div>';
1294
+
1295
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
1296
+ + ' data-dimensions="sent"'
1297
+ + ' data-chart-library="easypiechart"'
1298
+ + ' data-title="Net Outbound"'
1299
+ + ' data-width="11%"'
1300
+ + ' data-before="0"'
1301
+ + ' data-after="-' + duration.toString() + '"'
1302
+ + ' data-points="' + duration.toString() + '"'
1303
+ + ' data-common-units="system.net.mainhead"'
1304
+ + ' role="application"></div>';
1305
+ }
1306
+ else if (typeof charts['system.ip'] !== 'undefined') {
1307
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
1308
+ + ' data-dimensions="received"'
1309
+ + ' data-chart-library="easypiechart"'
1310
+ + ' data-title="IP Inbound"'
1311
+ + ' data-width="11%"'
1312
+ + ' data-before="0"'
1313
+ + ' data-after="-' + duration.toString() + '"'
1314
+ + ' data-points="' + duration.toString() + '"'
1315
+ + ' data-common-units="system.ip.mainhead"'
1316
+ + ' role="application"></div>';
1317
+
1318
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
1319
+ + ' data-dimensions="sent"'
1320
+ + ' data-chart-library="easypiechart"'
1321
+ + ' data-title="IP Outbound"'
1322
+ + ' data-width="11%"'
1323
+ + ' data-before="0"'
1324
+ + ' data-after="-' + duration.toString() + '"'
1325
+ + ' data-points="' + duration.toString() + '"'
1326
+ + ' data-common-units="system.ip.mainhead"'
1327
+ + ' role="application"></div>';
1328
+ }
1329
+ else if (typeof charts['system.ipv4'] !== 'undefined') {
1330
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
1331
+ + ' data-dimensions="received"'
1332
+ + ' data-chart-library="easypiechart"'
1333
+ + ' data-title="IPv4 Inbound"'
1334
+ + ' data-width="11%"'
1335
+ + ' data-before="0"'
1336
+ + ' data-after="-' + duration.toString() + '"'
1337
+ + ' data-points="' + duration.toString() + '"'
1338
+ + ' data-common-units="system.ipv4.mainhead"'
1339
+ + ' role="application"></div>';
1340
+
1341
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
1342
+ + ' data-dimensions="sent"'
1343
+ + ' data-chart-library="easypiechart"'
1344
+ + ' data-title="IPv4 Outbound"'
1345
+ + ' data-width="11%"'
1346
+ + ' data-before="0"'
1347
+ + ' data-after="-' + duration.toString() + '"'
1348
+ + ' data-points="' + duration.toString() + '"'
1349
+ + ' data-common-units="system.ipv4.mainhead"'
1350
+ + ' role="application"></div>';
1351
+ }
1352
+ else if (typeof charts['system.ipv6'] !== 'undefined') {
1353
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
1354
+ + ' data-dimensions="received"'
1355
+ + ' data-chart-library="easypiechart"'
1356
+ + ' data-title="IPv6 Inbound"'
1357
+ + ' data-units="kbps"'
1358
+ + ' data-width="11%"'
1359
+ + ' data-before="0"'
1360
+ + ' data-after="-' + duration.toString() + '"'
1361
+ + ' data-points="' + duration.toString() + '"'
1362
+ + ' data-common-units="system.ipv6.mainhead"'
1363
+ + ' role="application"></div>';
1364
+
1365
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
1366
+ + ' data-dimensions="sent"'
1367
+ + ' data-chart-library="easypiechart"'
1368
+ + ' data-title="IPv6 Outbound"'
1369
+ + ' data-units="kbps"'
1370
+ + ' data-width="11%"'
1371
+ + ' data-before="0"'
1372
+ + ' data-after="-' + duration.toString() + '"'
1373
+ + ' data-points="' + duration.toString() + '"'
1374
+ + ' data-common-units="system.ipv6.mainhead"'
1375
+ + ' role="application"></div>';
1376
+ }
1377
+
1378
+ if (typeof charts['system.ram'] !== 'undefined') {
1379
+ head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ram"'
1380
+ + ' data-dimensions="used|buffers|active|wired"' // active and wired are FreeBSD stats
1381
+ + ' data-append-options="percentage"'
1382
+ + ' data-chart-library="easypiechart"'
1383
+ + ' data-title="Used RAM"'
1384
+ + ' data-units="%"'
1385
+ + ' data-easypiechart-max-value="100"'
1386
+ + ' data-width="9%"'
1387
+ + ' data-after="-' + duration.toString() + '"'
1388
+ + ' data-points="' + duration.toString() + '"'
1389
+ + ' data-colors="' + NETDATA.colors[7] + '"'
1390
+ + ' role="application"></div>';
1391
+ }
1392
+
1393
+ return head;
1394
+}
1395
+
1396
+function generateHeadCharts(type, chart, duration) {
1397
+ if (urlOptions.mode === 'print') {
1398
+ return '';
1399
+ }
1400
+
1401
+ var head = '';
1402
+ var hcharts = netdataDashboard.anyAttribute(netdataDashboard.context, type, chart.context, []);
1403
+ if (hcharts.length > 0) {
1404
+ var hi = 0, hlen = hcharts.length;
1405
+ while (hi < hlen) {
1406
+ if (typeof hcharts[hi] === 'function') {
1407
+ head += hcharts[hi](netdataDashboard.os, chart.id).replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id);
1408
+ } else {
1409
+ head += hcharts[hi].replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id);
1410
+ }
1411
+ hi++;
1412
+ }
1413
+ }
1414
+ return head;
1415
+}
1416
+
1417
+function renderPage(menus, data) {
1418
+ var div = document.getElementById('charts_div');
1419
+ var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
1420
+
1421
+ // find the proper duration for per-second updates
1422
+ var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
1423
+ options.duration = duration;
1424
+ options.update_every = data.update_every;
1425
+
1426
+ var html = '';
1427
+ var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">';
1428
+ var mainhead = headMain(netdataDashboard.os, data.charts, duration);
1429
+
1430
+ // sort the menus
1431
+ var main = sortObjectByPriority(menus);
1432
+ var i = 0, len = main.length;
1433
+ while (i < len) {
1434
+ var menu = main[i++];
1435
+
1436
+ // generate an entry at the main menu
1437
+
1438
+ var menuid = NETDATA.name2id('menu_' + menu);
1439
+ sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].icon + ' ' + menus[menu].title + '</a><ul class="nav">';
1440
+ html += '<div role="section" class="dashboard-section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].icon + ' ' + menus[menu].title + '</h1></div><div role="section" class="dashboard-subsection">';
1441
+
1442
+ if (menus[menu].info !== null) {
1443
+ html += menus[menu].info;
1444
+ }
1445
+
1446
+ // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title);
1447
+
1448
+ var shtml = '';
1449
+ var mhead = '<div class="netdata-chart-row">' + mainhead;
1450
+ mainhead = '';
1451
+
1452
+ // sort the submenus of this menu
1453
+ var sub = sortObjectByPriority(menus[menu].submenus);
1454
+ var si = 0, slen = sub.length;
1455
+ while (si < slen) {
1456
+ var submenu = sub[si++];
1457
+
1458
+ // generate an entry at the submenu
1459
+ var submenuid = NETDATA.name2id('menu_' + menu + '_submenu_' + submenu);
1460
+ sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>';
1461
+ shtml += '<div role="section" class="dashboard-section-container" id="' + submenuid + '"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>';
1462
+
1463
+ if (menus[menu].submenus[submenu].info !== null) {
1464
+ shtml += '<div class="dashboard-submenu-info netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>';
1465
+ }
1466
+
1467
+ var head = '<div class="netdata-chart-row">';
1468
+ var chtml = '';
1469
+
1470
+ // console.log(' \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title);
1471
+
1472
+ // sort the charts in this submenu of this menu
1473
+ menus[menu].submenus[submenu].charts.sort(prioritySort);
1474
+ var ci = 0, clen = menus[menu].submenus[submenu].charts.length;
1475
+ while (ci < clen) {
1476
+ var chart = menus[menu].submenus[submenu].charts[ci++];
1477
+
1478
+ // generate the submenu heading charts
1479
+ mhead += generateHeadCharts('mainheads', chart, duration);
1480
+ head += generateHeadCharts('heads', chart, duration);
1481
+
1482
+ function chartCommonMin(family, context, units) {
1483
+ var x = netdataDashboard.anyAttribute(netdataDashboard.context, 'commonMin', context, undefined);
1484
+ if (typeof x !== 'undefined') {
1485
+ return ' data-common-min="' + family + '/' + context + '/' + units + '"';
1486
+ } else {
1487
+ return '';
1488
+ }
1489
+ }
1490
+
1491
+ function chartCommonMax(family, context, units) {
1492
+ var x = netdataDashboard.anyAttribute(netdataDashboard.context, 'commonMax', context, undefined);
1493
+ if (typeof x !== 'undefined') {
1494
+ return ' data-common-max="' + family + '/' + context + '/' + units + '"';
1495
+ } else {
1496
+ return '';
1497
+ }
1498
+ }
1499
+
1500
+ // generate the chart
1501
+ if (urlOptions.mode === 'print') {
1502
+ chtml += '<div role="row" class="dashboard-print-row">';
1503
+ }
1504
+
1505
+ chtml += '<div class="netdata-chartblock-container" style="width: ' + pcent_width.toString() + '%;">' + netdataDashboard.contextInfo(chart.context) + '<div class="netdata-container" id="chart_' + NETDATA.name2id(chart.id) + '" data-netdata="' + chart.id + '"'
1506
+ + ' data-width="100%"'
1507
+ + ' data-height="' + netdataDashboard.contextHeight(chart.context, options.chartsHeight).toString() + 'px"'
1508
+ + ' data-dygraph-valuerange="' + netdataDashboard.contextValueRange(chart.context) + '"'
1509
+ + ' data-before="0"'
1510
+ + ' data-after="-' + duration.toString() + '"'
1511
+ + ' data-id="' + NETDATA.name2id(options.hostname + '/' + chart.id) + '"'
1512
+ + ' data-colors="' + netdataDashboard.anyAttribute(netdataDashboard.context, 'colors', chart.context, '') + '"'
1513
+ + ' data-decimal-digits="' + netdataDashboard.contextDecimalDigits(chart.context, -1) + '"'
1514
+ + chartCommonMin(chart.family, chart.context, chart.units)
1515
+ + chartCommonMax(chart.family, chart.context, chart.units)
1516
+ + ' role="application"></div></div>';
1517
+
1518
+ if (urlOptions.mode === 'print') {
1519
+ chtml += '</div>';
1520
+ }
1521
+
1522
+ // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height);
1523
+ }
1524
+
1525
+ head += '</div>';
1526
+ shtml += head + chtml + '</div>';
1527
+ }
1528
+
1529
+ mhead += '</div>';
1530
+ sidebar += '</ul></li>';
1531
+ html += mhead + shtml + '</div></div><hr role="separator"/>';
1532
+ }
1533
+
1534
+ sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/netdata/netdata/wiki/Add-more-charts-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more charts</a></li>';
1535
+ sidebar += '<li class=""><a href="https://github.com/netdata/netdata/wiki/Add-more-alarms-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more alarms</a></li>';
1536
+ sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every === 1) ? 'second' : data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, {space: ' '}) + ' of real-time history.<br/> <br/><b>netdata</b><br/>v' + data.version.toString() + '</small></li>';
1537
+ sidebar += '</ul>';
1538
+ div.innerHTML = html;
1539
+ document.getElementById('sidebar').innerHTML = sidebar;
1540
+
1541
+ if (urlOptions.highlight === true) {
1542
+ NETDATA.globalChartUnderlay.init(null
1543
+ , urlOptions.highlight_after
1544
+ , urlOptions.highlight_before
1545
+ , (urlOptions.after > 0) ? urlOptions.after : null
1546
+ , (urlOptions.before > 0) ? urlOptions.before : null
1547
+ );
1548
+ } else {
1549
+ NETDATA.globalChartUnderlay.clear();
1550
+ }
1551
+
1552
+ if (urlOptions.mode === 'print') {
1553
+ printPage();
1554
+ } else {
1555
+ finalizePage();
1556
+ }
1557
+}
1558
+
1559
+function renderChartsAndMenu(data) {
1560
+ options.menus = {};
1561
+ options.submenu_names = {};
1562
+
1563
+ var menus = options.menus;
1564
+ var charts = data.charts;
1565
+ var m, menu_key;
1566
+
1567
+ for (var c in charts) {
1568
+ if (!charts.hasOwnProperty(c)) {
1569
+ continue;
1570
+ }
1571
+
1572
+ var chart = charts[c];
1573
+ enrichChartData(chart);
1574
+ m = chart.menu;
1575
+
1576
+ // create the menu
1577
+ if (typeof menus[m] === 'undefined') {
1578
+ menus[m] = {
1579
+ menu_pattern: chart.menu_pattern,
1580
+ priority: chart.priority,
1581
+ submenus: {},
1582
+ title: netdataDashboard.menuTitle(chart),
1583
+ icon: netdataDashboard.menuIcon(chart),
1584
+ info: netdataDashboard.menuInfo(chart),
1585
+ height: netdataDashboard.menuHeight(chart) * options.chartsHeight
1586
+ };
1587
+ }
1588
+ else {
1589
+ if (typeof (menus[m].menu_pattern) === 'undefined') {
1590
+ menus[m].menu_pattern = chart.menu_pattern;
1591
+ }
1592
+
1593
+ if (chart.priority < menus[m].priority) {
1594
+ menus[m].priority = chart.priority;
1595
+ }
1596
+ }
1597
+
1598
+ menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m;
1599
+
1600
+ // create the submenu
1601
+ if (typeof menus[m].submenus[chart.submenu] === 'undefined') {
1602
+ menus[m].submenus[chart.submenu] = {
1603
+ priority: chart.priority,
1604
+ charts: [],
1605
+ title: null,
1606
+ info: netdataDashboard.submenuInfo(menu_key, chart.submenu),
1607
+ height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height)
1608
+ };
1609
+ }
1610
+ else {
1611
+ if (chart.priority < menus[m].submenus[chart.submenu].priority) {
1612
+ menus[m].submenus[chart.submenu].priority = chart.priority;
1613
+ }
1614
+ }
1615
+
1616
+ // index the chart in the menu/submenu
1617
+ menus[m].submenus[chart.submenu].charts.push(chart);
1618
+ }
1619
+
1620
+ // propagate the descriptive subname given to QoS
1621
+ // to all the other submenus with the same name
1622
+ for (m in menus) {
1623
+ if (!menus.hasOwnProperty(m)) {
1624
+ continue;
1625
+ }
1626
+
1627
+ for (var s in menus[m].submenus) {
1628
+ if (!menus[m].submenus.hasOwnProperty(s)) {
1629
+ continue;
1630
+ }
1631
+
1632
+ // set the family using a name
1633
+ if (typeof options.submenu_names[s] !== 'undefined') {
1634
+ menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')';
1635
+ }
1636
+ else {
1637
+ menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m;
1638
+ menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s);
1639
+ }
1640
+ }
1641
+ }
1642
+
1643
+ renderPage(menus, data);
1644
+}
1645
+
1646
+// ----------------------------------------------------------------------------
1647
+
1648
+function loadJs(url, callback) {
1649
+ $.ajax({
1650
+ url: url,
1651
+ cache: true,
1652
+ dataType: "script",
1653
+ xhrFields: {withCredentials: true} // required for the cookie
1654
+ })
1655
+ .fail(function () {
1656
+ alert('Cannot load required JS library: ' + url);
1657
+ })
1658
+ .always(function () {
1659
+ if (typeof callback === 'function') {
1660
+ callback();
1661
+ }
1662
+ })
1663
+}
1664
+
1665
+var clipboardLoaded = false;
1666
+
1667
+function loadClipboard(callback) {
1668
+ if (clipboardLoaded === false) {
1669
+ clipboardLoaded = true;
1670
+ loadJs('lib/clipboard-polyfill-be05dad.js', callback);
1671
+ }
1672
+ else {
1673
+ callback();
1674
+ }
1675
+}
1676
+
1677
+var bootstrapTableLoaded = false;
1678
+
1679
+function loadBootstrapTable(callback) {
1680
+ if (bootstrapTableLoaded === false) {
1681
+ bootstrapTableLoaded = true;
1682
+ loadJs('lib/bootstrap-table-1.11.0.min.js', function () {
1683
+ loadJs('lib/bootstrap-table-export-1.11.0.min.js', function () {
1684
+ loadJs('lib/tableExport-1.6.0.min.js', callback);
1685
+ })
1686
+ });
1687
+ }
1688
+ else {
1689
+ callback();
1690
+ }
1691
+}
1692
+
1693
+var bootstrapSliderLoaded = false;
1694
+
1695
+function loadBootstrapSlider(callback) {
1696
+ if (bootstrapSliderLoaded === false) {
1697
+ bootstrapSliderLoaded = true;
1698
+ loadJs('lib/bootstrap-slider-10.0.0.min.js', function () {
1699
+ NETDATA._loadCSS('css/bootstrap-slider-10.0.0.min.css');
1700
+ callback();
1701
+ });
1702
+ }
1703
+ else {
1704
+ callback();
1705
+ }
1706
+}
1707
+
1708
+var lzStringLoaded = false;
1709
+
1710
+function loadLzString(callback) {
1711
+ if (lzStringLoaded === false) {
1712
+ lzStringLoaded = true;
1713
+ loadJs('lib/lz-string-1.4.4.min.js', callback);
1714
+ }
1715
+ else {
1716
+ callback();
1717
+ }
1718
+}
1719
+
1720
+var pakoLoaded = false;
1721
+
1722
+function loadPako(callback) {
1723
+ if (pakoLoaded === false) {
1724
+ pakoLoaded = true;
1725
+ loadJs('lib/pako-1.0.6.min.js', callback);
1726
+ }
1727
+ else {
1728
+ callback();
1729
+ }
1730
+}
1731
+
1732
+// ----------------------------------------------------------------------------
1733
+
1734
+function clipboardCopy(text) {
1735
+ clipboard.writeText(text);
1736
+}
1737
+
1738
+function clipboardCopyBadgeEmbed(url) {
1739
+ clipboard.writeText('<embed src="' + url + '" type="image/svg+xml" height="20"/>');
1740
+}
1741
+
1742
+// ----------------------------------------------------------------------------
1743
+
1744
+function alarmsUpdateModal() {
1745
+ var active = '<h3>Raised Alarms</h3><table class="table">';
1746
+ var all = '<h3>All Running Alarms</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">';
1747
+ var footer = '<hr/><a href="https://github.com/netdata/netdata/tree/master/web/api/badges#netdata-badges" target="_blank">netdata badges</a> refresh automatically. Their color indicates the state of the alarm: <span style="color: #e05d44"><b> red </b></span> is critical, <span style="color:#fe7d37"><b> orange </b></span> is warning, <span style="color: #4c1"><b> bright green </b></span> is ok, <span style="color: #9f9f9f"><b> light grey </b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b> black </b></span> is not initialized. You can copy and paste their URLs to embed them in any web page.<br/>netdata can send notifications for these alarms. Check <a href="https://github.com/netdata/netdata/blob/master/health/notifications/health_alarm_notify.conf">this configuration file</a> for more information.';
1748
+
1749
+ loadClipboard(function () {
1750
+ });
1751
+
1752
+ NETDATA.alarms.get('all', function (data) {
1753
+ options.alarm_families = [];
1754
+
1755
+ alarmsCallback(data);
1756
+
1757
+ if (data === null) {
1758
+ document.getElementById('alarms_active').innerHTML =
1759
+ document.getElementById('alarms_all').innerHTML =
1760
+ document.getElementById('alarms_log').innerHTML =
1761
+ 'failed to load alarm data!';
1762
+ return;
1763
+ }
1764
+
1765
+ function alarmid4human(id) {
1766
+ if (id === 0) {
1767
+ return '-';
1768
+ }
1769
+
1770
+ return id.toString();
1771
+ }
1772
+
1773
+ function timestamp4human(timestamp, space) {
1774
+ if (timestamp === 0) {
1775
+ return '-';
1776
+ }
1777
+
1778
+ if (typeof space === 'undefined') {
1779
+ space = ' ';
1780
+ }
1781
+
1782
+ var t = new Date(timestamp * 1000);
1783
+ var now = new Date();
1784
+
1785
+ if (t.toDateString() === now.toDateString()) {
1786
+ return t.toLocaleTimeString();
1787
+ }
1788
+
1789
+ return t.toLocaleDateString() + space + t.toLocaleTimeString();
1790
+ }
1791
+
1792
+ function alarm_lookup_explain(alarm, chart) {
1793
+ var dimensions = ' of all values ';
1794
+
1795
+ if (chart.dimensions.length > 1) {
1796
+ dimensions = ' of the sum of all dimensions ';
1797
+ }
1798
+
1799
+ if (typeof alarm.lookup_dimensions !== 'undefined') {
1800
+ var d = alarm.lookup_dimensions.replace(/|/g, ',');
1801
+ var x = d.split(',');
1802
+ if (x.length > 1) {
1803
+ dimensions = 'of the sum of dimensions <code>' + alarm.lookup_dimensions + '</code> ';
1804
+ } else {
1805
+ dimensions = 'of all values of dimension <code>' + alarm.lookup_dimensions + '</code> ';
1806
+ }
1807
+ }
1808
+
1809
+ return '<code>' + alarm.lookup_method + '</code> '
1810
+ + dimensions + ', of chart <code>' + alarm.chart + '</code>'
1811
+ + ', starting <code>' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, {space: ' '}) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, {space: ' '}) + '</code>'
1812
+ + ((alarm.lookup_options) ? (', with options <code>' + alarm.lookup_options.replace(/ /g, ', ') + '</code>') : '')
1813
+ + '.';
1814
+ }
1815
+
1816
+ function alarm_to_html(alarm, full) {
1817
+ var chart = options.data.charts[alarm.chart];
1818
+ if (typeof (chart) === 'undefined') {
1819
+ chart = options.data.charts_by_name[alarm.chart];
1820
+ if (typeof (chart) === 'undefined') {
1821
+ // this means the charts loaded are incomplete
1822
+ // probably netdata was restarted and more alarms
1823
+ // are now available.
1824
+ console.log('Cannot find chart ' + alarm.chart + ', you probably need to refresh the page.');
1825
+ return '';
1826
+ }
1827
+ }
1828
+
1829
+ var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined');
1830
+ var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto';
1831
+
1832
+ var action_buttons = '<br/> <br/>role: <b>' + alarm.recipient + '</b><br/> <br/>'
1833
+ + '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm.chart + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
1834
+ + '<div class="action-button ripple" title="click to copy to the clipboard the URL of this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopy(\'' + badge_url + '\'); return false;"><i class="far fa-copy"></i></div>'
1835
+ + '<div class="action-button ripple" title="click to copy to the clipboard an auto-refreshing <code>embed</code> html element for this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopyBadgeEmbed(\'' + badge_url + '\'); return false;"><i class="fas fa-copy"></i></div>';
1836
+
1837
+ var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/> <br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/> <br/><span style="font-size: 18px">' + alarm.info + '</span>' + action_buttons + '</td>'
1838
+ + '<td><table class="table">'
1839
+ + ((typeof alarm.warn !== 'undefined') ? ('<tr><td width="10%" style="text-align:right">warning when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>') : '')
1840
+ + ((typeof alarm.crit !== 'undefined') ? ('<tr><td width="10%" style="text-align:right">critical when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>') : '');
1841
+
1842
+ if (full === true) {
1843
+ var units = chart.units;
1844
+ if (units === '%') {
1845
+ units = '%';
1846
+ }
1847
+
1848
+ html += ((typeof alarm.lookup_after !== 'undefined') ? ('<tr><td width="10%" style="text-align:right">db lookup</td><td>' + alarm_lookup_explain(alarm, chart) + '</td></tr>') : '')
1849
+ + ((typeof alarm.calc !== 'undefined') ? ('<tr><td width="10%" style="text-align:right">calculation</td><td><span style="font-family: monospace;">' + alarm.calc + '</span></td></tr>') : '')
1850
+ + ((chart.green !== null) ? ('<tr><td width="10%" style="text-align:right">green threshold</td><td><code>' + chart.green + ' ' + units + '</code></td></tr>') : '')
1851
+ + ((chart.red !== null) ? ('<tr><td width="10%" style="text-align:right">red threshold</td><td><code>' + chart.red + ' ' + units + '</code></td></tr>') : '');
1852
+ }
1853
+
1854
+ var delay = '';
1855
+ if ((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) {
1856
+ if (alarm.delay_up_duration === alarm.delay_down_duration) {
1857
+ delay += '<small><br/>hysteresis ' + NETDATA.seconds4human(alarm.delay_up_duration, {
1858
+ space: ' ',
1859
+ negative_suffix: ''
1860
+ });
1861
+ }
1862
+ else {
1863
+ delay = '<small><br/>hysteresis ';
1864
+ if (alarm.delay_up_duration > 0) {
1865
+ delay += 'on escalation <code>' + NETDATA.seconds4human(alarm.delay_up_duration, {
1866
+ space: ' ',
1867
+ negative_suffix: ''
1868
+ }) + '</code>, ';
1869
+ }
1870
+ if (alarm.delay_down_duration > 0) {
1871
+ delay += 'on recovery <code>' + NETDATA.seconds4human(alarm.delay_down_duration, {
1872
+ space: ' ',
1873
+ negative_suffix: ''
1874
+ }) + '</code>, ';
1875
+ }
1876
+ }
1877
+ if (alarm.delay_multiplier !== 1.0) {
1878
+ delay += 'multiplied by <code>' + alarm.delay_multiplier.toString() + '</code>';
1879
+ delay += ', up to <code>' + NETDATA.seconds4human(alarm.delay_max_duration, {
1880
+ space: ' ',
1881
+ negative_suffix: ''
1882
+ }) + '</code>';
1883
+ }
1884
+ delay += '</small>';
1885
+ }
1886
+
1887
+ html += '<tr><td width="10%" style="text-align:right">check every</td><td>' + NETDATA.seconds4human(alarm.update_every, {
1888
+ space: ' ',
1889
+ negative_suffix: ''
1890
+ }) + '</td></tr>'
1891
+ + ((has_alarm === true) ? ('<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>') : '')
1892
+ + '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>'
1893
+ + '</table></td></tr>';
1894
+
1895
+ return html;
1896
+ }
1897
+
1898
+ function alarm_family_show(id) {
1899
+ var html = '<table class="table">';
1900
+ var family = options.alarm_families[id];
1901
+ var len = family.arr.length;
1902
+ while (len--) {
1903
+ var alarm = family.arr[len];
1904
+ html += alarm_to_html(alarm, true);
1905
+ }
1906
+ html += '</table>';
1907
+
1908
+ $('#alarm_all_' + id.toString()).html(html);
1909
+ enableTooltipsAndPopovers();
1910
+ }
1911
+
1912
+ // find the proper family of each alarm
1913
+ var x, family, alarm;
1914
+ var count_active = 0;
1915
+ var count_all = 0;
1916
+ var families = {};
1917
+ var families_sort = [];
1918
+ for (x in data.alarms) {
1919
+ if (!data.alarms.hasOwnProperty(x)) {
1920
+ continue;
1921
+ }
1922
+
1923
+ alarm = data.alarms[x];
1924
+ family = alarm.family;
1925
+
1926
+ // find the chart
1927
+ var chart = options.data.charts[alarm.chart];
1928
+ if (typeof chart === 'undefined') {
1929
+ chart = options.data.charts_by_name[alarm.chart];
1930
+ }
1931
+
1932
+ // not found - this should never happen!
1933
+ if (typeof chart === 'undefined') {
1934
+ console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.');
1935
+ chart = {priority: 9999999};
1936
+ }
1937
+ else if (typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined')
1938
+ // the family based on the chart
1939
+ {
1940
+ family = chart.menu + ' - ' + chart.submenu;
1941
+ }
1942
+
1943
+ if (typeof families[family] === 'undefined') {
1944
+ families[family] = {
1945
+ name: family,
1946
+ arr: [],
1947
+ priority: chart.priority
1948
+ };
1949
+
1950
+ families_sort.push(families[family]);
1951
+ }
1952
+
1953
+ if (chart.priority < families[family].priority) {
1954
+ families[family].priority = chart.priority;
1955
+ }
1956
+
1957
+ families[family].arr.unshift(alarm);
1958
+ }
1959
+
1960
+ // sort the families, like the dashboard menu does
1961
+ var families_sorted = families_sort.sort(function (a, b) {
1962
+ if (a.priority < b.priority) {
1963
+ return -1;
1964
+ }
1965
+ if (a.priority > b.priority) {
1966
+ return 1;
1967
+ }
1968
+ return naturalSortCompare(a.name, b.name);
1969
+ });
1970
+
1971
+ var i = 0;
1972
+ var fc = 0;
1973
+ var len = families_sorted.length;
1974
+ while (len--) {
1975
+ family = families_sorted[i++].name;
1976
+ var active_family_added = false;
1977
+ var expanded = 'true';
1978
+ var collapsed = '';
1979
+ var cin = 'in';
1980
+
1981
+ if (fc !== 0) {
1982
+ all += "</table></div></div></div>";
1983
+ expanded = 'false';
1984
+ collapsed = 'class="collapsed"';
1985
+ cin = '';
1986
+ }
1987
+
1988
+ all += '<div class="panel panel-default"><div class="panel-heading" role="tab" id="alarm_all_heading_' + fc.toString() + '"><h4 class="panel-title"><a ' + collapsed + ' role="button" data-toggle="collapse" data-parent="#alarms_all_accordion" href="#alarm_all_' + fc.toString() + '" aria-expanded="' + expanded + '" aria-controls="alarm_all_' + fc.toString() + '">' + family.toString() + '</a></h4></div><div id="alarm_all_' + fc.toString() + '" class="panel-collapse collapse ' + cin + '" role="tabpanel" aria-labelledby="alarm_all_heading_' + fc.toString() + '" data-alarm-id="' + fc.toString() + '"><div class="panel-body" id="alarm_all_body_' + fc.toString() + '">';
1989
+
1990
+ options.alarm_families[fc] = families[family];
1991
+
1992
+ fc++;
1993
+
1994
+ var arr = families[family].arr;
1995
+ var c = arr.length;
1996
+ while (c--) {
1997
+ alarm = arr[c];
1998
+ if (alarm.status === 'WARNING' || alarm.status === 'CRITICAL') {
1999
+ if (!active_family_added) {
2000
+ active_family_added = true;
2001
+ active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>';
2002
+ }
2003
+ count_active++;
2004
+ active += alarm_to_html(alarm, true);
2005
+ }
2006
+
2007
+ count_all++;
2008
+ }
2009
+ }
2010
+ active += "</table>";
2011
+ if (families_sorted.length > 0) {
2012
+ all += "</div></div></div>";
2013
+ }
2014
+ all += "</div>";
2015
+
2016
+ if (!count_active) {
2017
+ active += '<div style="width:100%; height: 100px; text-align: center;"><span style="font-size: 50px;"><i class="fas fa-thumbs-up"></i></span><br/>Everything is normal. No raised alarms.</div>';
2018
+ } else {
2019
+ active += footer;
2020
+ }
2021
+
2022
+ if (!count_all) {
2023
+ all += "<h4>No alarms are running in this system.</h4>";
2024
+ } else {
2025
+ all += footer;
2026
+ }
2027
+
2028
+ document.getElementById('alarms_active').innerHTML = active;
2029
+ document.getElementById('alarms_all').innerHTML = all;
2030
+ enableTooltipsAndPopovers();
2031
+
2032
+ if (families_sorted.length > 0) {
2033
+ alarm_family_show(0);
2034
+ }
2035
+
2036
+ // register bootstrap events
2037
+ var $accordion = $('#alarms_all_accordion');
2038
+ $accordion.on('show.bs.collapse', function (d) {
2039
+ var target = $(d.target);
2040
+ var id = $(target).data('alarm-id');
2041
+ alarm_family_show(id);
2042
+ });
2043
+ $accordion.on('hidden.bs.collapse', function (d) {
2044
+ var target = $(d.target);
2045
+ var id = $(target).data('alarm-id');
2046
+ $('#alarm_all_' + id.toString()).html('');
2047
+ });
2048
+
2049
+ document.getElementById('alarms_log').innerHTML = '<h3>Alarm Log</h3><table id="alarms_log_table"></table>';
2050
+
2051
+ loadBootstrapTable(function () {
2052
+ $('#alarms_log_table').bootstrapTable({
2053
+ url: NETDATA.alarms.server + '/api/v1/alarm_log?all',
2054
+ cache: false,
2055
+ pagination: true,
2056
+ pageSize: 10,
2057
+ showPaginationSwitch: false,
2058
+ search: true,
2059
+ searchTimeOut: 300,
2060
+ searchAlign: 'left',
2061
+ showColumns: true,
2062
+ showExport: true,
2063
+ exportDataType: 'basic',
2064
+ exportOptions: {
2065
+ fileName: 'netdata_alarm_log'
2066
+ },
2067
+ rowStyle: function (row, index) {
2068
+ void (index);
2069
+
2070
+ switch (row.status) {
2071
+ case 'CRITICAL':
2072
+ return {classes: 'danger'};
2073
+ break;
2074
+ case 'WARNING':
2075
+ return {classes: 'warning'};
2076
+ break;
2077
+ case 'UNDEFINED':
2078
+ return {classes: 'info'};
2079
+ break;
2080
+ case 'CLEAR':
2081
+ return {classes: 'success'};
2082
+ break;
2083
+ }
2084
+ return {};
2085
+ },
2086
+ showFooter: false,
2087
+ showHeader: true,
2088
+ showRefresh: true,
2089
+ showToggle: false,
2090
+ sortable: true,
2091
+ silentSort: false,
2092
+ columns: [
2093
+ {
2094
+ field: 'when',
2095
+ title: 'Event Date',
2096
+ valign: 'middle',
2097
+ titleTooltip: 'The date and time the even took place',
2098
+ formatter: function (value, row, index) {
2099
+ void (row);
2100
+ void (index);
2101
+ return timestamp4human(value, ' ');
2102
+ },
2103
+ align: 'center',
2104
+ switchable: false,
2105
+ sortable: true
2106
+ },
2107
+ {
2108
+ field: 'hostname',
2109
+ title: 'Host',
2110
+ valign: 'middle',
2111
+ titleTooltip: 'The host that generated this event',
2112
+ align: 'center',
2113
+ visible: false,
2114
+ sortable: true
2115
+ },
2116
+ {
2117
+ field: 'unique_id',
2118
+ title: 'Unique ID',
2119
+ titleTooltip: 'The host unique ID for this event',
2120
+ formatter: function (value, row, index) {
2121
+ void (row);
2122
+ void (index);
2123
+ return alarmid4human(value);
2124
+ },
2125
+ align: 'center',
2126
+ valign: 'middle',
2127
+ visible: false,
2128
+ sortable: true
2129
+ },
2130
+ {
2131
+ field: 'alarm_id',
2132
+ title: 'Alarm ID',
2133
+ titleTooltip: 'The ID of the alarm that generated this event',
2134
+ formatter: function (value, row, index) {
2135
+ void (row);
2136
+ void (index);
2137
+ return alarmid4human(value);
2138
+ },
2139
+ align: 'center',
2140
+ valign: 'middle',
2141
+ visible: false,
2142
+ sortable: true
2143
+ },
2144
+ {
2145
+ field: 'alarm_event_id',
2146
+ title: 'Alarm Event ID',
2147
+ titleTooltip: 'The incremental ID of this event for the given alarm',
2148
+ formatter: function (value, row, index) {
2149
+ void (row);
2150
+ void (index);
2151
+ return alarmid4human(value);
2152
+ },
2153
+ align: 'center',
2154
+ valign: 'middle',
2155
+ visible: false,
2156
+ sortable: true
2157
+ },
2158
+ {
2159
+ field: 'chart',
2160
+ title: 'Chart',
2161
+ titleTooltip: 'The chart the alarm is attached to',
2162
+ align: 'center',
2163
+ valign: 'middle',
2164
+ switchable: false,
2165
+ sortable: true
2166
+ },
2167
+ {
2168
+ field: 'family',
2169
+ title: 'Family',
2170
+ titleTooltip: 'The family of the chart the alarm is attached to',
2171
+ align: 'center',
2172
+ valign: 'middle',
2173
+ visible: false,
2174
+ sortable: true
2175
+ },
2176
+ {
2177
+ field: 'name',
2178
+ title: 'Alarm',
2179
+ titleTooltip: 'The alarm name that generated this event',
2180
+ formatter: function (value, row, index) {
2181
+ void (row);
2182
+ void (index);
2183
+ return value.toString().replace(/_/g, ' ');
2184
+ },
2185
+ align: 'center',
2186
+ valign: 'middle',
2187
+ switchable: false,
2188
+ sortable: true
2189
+ },
2190
+ {
2191
+ field: 'value_string',
2192
+ title: 'Friendly Value',
2193
+ titleTooltip: 'The value of the alarm, that triggered this event',
2194
+ align: 'right',
2195
+ valign: 'middle',
2196
+ sortable: true
2197
+ },
2198
+ {
2199
+ field: 'old_value_string',
2200
+ title: 'Friendly Old Value',
2201
+ titleTooltip: 'The value of the alarm, just before this event',
2202
+ align: 'right',
2203
+ valign: 'middle',
2204
+ visible: false,
2205
+ sortable: true
2206
+ },
2207
+ {
2208
+ field: 'old_value',
2209
+ title: 'Old Value',
2210
+ titleTooltip: 'The value of the alarm, just before this event',
2211
+ formatter: function (value, row, index) {
2212
+ void (row);
2213
+ void (index);
2214
+ return ((value !== null) ? Math.round(value * 100) / 100 : 'NaN').toString();
2215
+ },
2216
+ align: 'center',
2217
+ valign: 'middle',
2218
+ visible: false,
2219
+ sortable: true
2220
+ },
2221
+ {
2222
+ field: 'value',
2223
+ title: 'Value',
2224
+ titleTooltip: 'The value of the alarm, that triggered this event',
2225
+ formatter: function (value, row, index) {
2226
+ void (row);
2227
+ void (index);
2228
+ return ((value !== null) ? Math.round(value * 100) / 100 : 'NaN').toString();
2229
+ },
2230
+ align: 'right',
2231
+ valign: 'middle',
2232
+ visible: false,
2233
+ sortable: true
2234
+ },
2235
+ {
2236
+ field: 'units',
2237
+ title: 'Units',
2238
+ titleTooltip: 'The units of the value of the alarm',
2239
+ align: 'left',
2240
+ valign: 'middle',
2241
+ visible: false,
2242
+ sortable: true
2243
+ },
2244
+ {
2245
+ field: 'old_status',
2246
+ title: 'Old Status',
2247
+ titleTooltip: 'The status of the alarm, just before this event',
2248
+ align: 'center',
2249
+ valign: 'middle',
2250
+ visible: false,
2251
+ sortable: true
2252
+ },
2253
+ {
2254
+ field: 'status',
2255
+ title: 'Status',
2256
+ titleTooltip: 'The status of the alarm, that was set due to this event',
2257
+ align: 'center',
2258
+ valign: 'middle',
2259
+ switchable: false,
2260
+ sortable: true
2261
+ },
2262
+ {
2263
+ field: 'duration',
2264
+ title: 'Last Duration',
2265
+ titleTooltip: 'The duration the alarm was at its previous state, just before this event',
2266
+ formatter: function (value, row, index) {
2267
+ void (row);
2268
+ void (index);
2269
+ return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'});
2270
+ },
2271
+ align: 'center',
2272
+ valign: 'middle',
2273
+ visible: false,
2274
+ sortable: true
2275
+ },
2276
+ {
2277
+ field: 'non_clear_duration',
2278
+ title: 'Raised Duration',
2279
+ titleTooltip: 'The duration the alarm was raised, just before this event',
2280
+ formatter: function (value, row, index) {
2281
+ void (row);
2282
+ void (index);
2283
+ return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'});
2284
+ },
2285
+ align: 'center',
2286
+ valign: 'middle',
2287
+ visible: false,
2288
+ sortable: true
2289
+ },
2290
+ {
2291
+ field: 'recipient',
2292
+ title: 'Recipient',
2293
+ titleTooltip: 'The recipient of this event',
2294
+ align: 'center',
2295
+ valign: 'middle',
2296
+ visible: false,
2297
+ sortable: true
2298
+ },
2299
+ {
2300
+ field: 'processed',
2301
+ title: 'Processed Status',
2302
+ titleTooltip: 'True when this event is processed',
2303
+ formatter: function (value, row, index) {
2304
+ void (row);
2305
+ void (index);
2306
+
2307
+ if (value === true) {
2308
+ return 'DONE';
2309
+ } else {
2310
+ return 'PENDING';
2311
+ }
2312
+ },
2313
+ align: 'center',
2314
+ valign: 'middle',
2315
+ visible: false,
2316
+ sortable: true
2317
+ },
2318
+ {
2319
+ field: 'updated',
2320
+ title: 'Updated Status',
2321
+ titleTooltip: 'True when this event has been updated by another event',
2322
+ formatter: function (value, row, index) {
2323
+ void (row);
2324
+ void (index);
2325
+
2326
+ if (value === true) {
2327
+ return 'UPDATED';
2328
+ } else {
2329
+ return 'CURRENT';
2330
+ }
2331
+ },
2332
+ align: 'center',
2333
+ valign: 'middle',
2334
+ visible: false,
2335
+ sortable: true
2336
+ },
2337
+ {
2338
+ field: 'updated_by_id',
2339
+ title: 'Updated By ID',
2340
+ titleTooltip: 'The unique ID of the event that obsoleted this one',
2341
+ formatter: function (value, row, index) {
2342
+ void (row);
2343
+ void (index);
2344
+ return alarmid4human(value);
2345
+ },
2346
+ align: 'center',
2347
+ valign: 'middle',
2348
+ visible: false,
2349
+ sortable: true
2350
+ },
2351
+ {
2352
+ field: 'updates_id',
2353
+ title: 'Updates ID',
2354
+ titleTooltip: 'The unique ID of the event obsoleted because of this event',
2355
+ formatter: function (value, row, index) {
2356
+ void (row);
2357
+ void (index);
2358
+ return alarmid4human(value);
2359
+ },
2360
+ align: 'center',
2361
+ valign: 'middle',
2362
+ visible: false,
2363
+ sortable: true
2364
+ },
2365
+ {
2366
+ field: 'exec',
2367
+ title: 'Script',
2368
+ titleTooltip: 'The script to handle the event notification',
2369
+ align: 'center',
2370
+ valign: 'middle',
2371
+ visible: false,
2372
+ sortable: true
2373
+ },
2374
+ {
2375
+ field: 'exec_run',
2376
+ title: 'Script Run At',
2377
+ titleTooltip: 'The date and time the script has been ran',
2378
+ formatter: function (value, row, index) {
2379
+ void (row);
2380
+ void (index);
2381
+ return timestamp4human(value, ' ');
2382
+ },
2383
+ align: 'center',
2384
+ valign: 'middle',
2385
+ visible: false,
2386
+ sortable: true
2387
+ },
2388
+ {
2389
+ field: 'exec_code',
2390
+ title: 'Script Return Value',
2391
+ titleTooltip: 'The return code of the script',
2392
+ formatter: function (value, row, index) {
2393
+ void (row);
2394
+ void (index);
2395
+
2396
+ if (value === 0) {
2397
+ return 'OK (returned 0)';
2398
+ } else {
2399
+ return 'FAILED (with code ' + value.toString() + ')';
2400
+ }
2401
+ },
2402
+ align: 'center',
2403
+ valign: 'middle',
2404
+ visible: false,
2405
+ sortable: true
2406
+ },
2407
+ {
2408
+ field: 'delay',
2409
+ title: 'Script Delay',
2410
+ titleTooltip: 'The hysteresis of the notification',
2411
+ formatter: function (value, row, index) {
2412
+ void (row);
2413
+ void (index);
2414
+
2415
+ return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'});
2416
+ },
2417
+ align: 'center',
2418
+ valign: 'middle',
2419
+ visible: false,
2420
+ sortable: true
2421
+ },
2422
+ {
2423
+ field: 'delay_up_to_timestamp',
2424
+ title: 'Script Delay Run At',
2425
+ titleTooltip: 'The date and time the script should be run, after hysteresis',
2426
+ formatter: function (value, row, index) {
2427
+ void (row);
2428
+ void (index);
2429
+ return timestamp4human(value, ' ');
2430
+ },
2431
+ align: 'center',
2432
+ valign: 'middle',
2433
+ visible: false,
2434
+ sortable: true
2435
+ },
2436
+ {
2437
+ field: 'info',
2438
+ title: 'Description',
2439
+ titleTooltip: 'A short description of the alarm',
2440
+ align: 'center',
2441
+ valign: 'middle',
2442
+ visible: false,
2443
+ sortable: true
2444
+ },
2445
+ {
2446
+ field: 'source',
2447
+ title: 'Alarm Source',
2448
+ titleTooltip: 'The source of configuration of the alarm',
2449
+ align: 'center',
2450
+ valign: 'middle',
2451
+ visible: false,
2452
+ sortable: true
2453
+ }
2454
+ ]
2455
+ });
2456
+ // console.log($('#alarms_log_table').bootstrapTable('getOptions'));
2457
+ });
2458
+ });
2459
+}
2460
+
2461
+function alarmsCallback(data) {
2462
+ var count = 0, x;
2463
+ for (x in data.alarms) {
2464
+ if (!data.alarms.hasOwnProperty(x)) {
2465
+ continue;
2466
+ }
2467
+
2468
+ var alarm = data.alarms[x];
2469
+ if (alarm.status === 'WARNING' || alarm.status === 'CRITICAL') {
2470
+ count++;
2471
+ }
2472
+ }
2473
+
2474
+ if (count > 0) {
2475
+ document.getElementById('alarms_count_badge').innerHTML = count.toString();
2476
+ } else {
2477
+ document.getElementById('alarms_count_badge').innerHTML = '';
2478
+ }
2479
+}
2480
+
2481
+function initializeDynamicDashboardWithData(data) {
2482
+ if (data !== null) {
2483
+ options.hostname = data.hostname;
2484
+ options.data = data;
2485
+ options.version = data.version;
2486
+ netdataDashboard.os = data.os;
2487
+
2488
+ if (typeof data.hosts !== 'undefined') {
2489
+ options.hosts = data.hosts;
2490
+ }
2491
+
2492
+ // update the dashboard hostname
2493
+ document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString();
2494
+ document.getElementById('hostname').href = NETDATA.serverDefault;
2495
+ document.getElementById('netdataVersion').innerHTML = options.version;
2496
+
2497
+ if (netdataSnapshotData !== null) {
2498
+ $('#alarmsButton').hide();
2499
+ $('#updateButton').hide();
2500
+ // $('#loadButton').hide();
2501
+ $('#saveButton').hide();
2502
+ $('#printButton').hide();
2503
+ }
2504
+
2505
+ // update the dashboard title
2506
+ document.title = options.hostname + ' netdata dashboard';
2507
+
2508
+ // close the splash screen
2509
+ $("#loadOverlay").css("display", "none");
2510
+
2511
+ // create a chart_by_name index
2512
+ data.charts_by_name = {};
2513
+ var charts = data.charts;
2514
+ var x;
2515
+ for (x in charts) {
2516
+ if (!charts.hasOwnProperty(x)) {
2517
+ continue;
2518
+ }
2519
+
2520
+ var chart = charts[x];
2521
+ data.charts_by_name[chart.name] = chart;
2522
+ }
2523
+
2524
+ // render all charts
2525
+ renderChartsAndMenu(data);
2526
+ }
2527
+}
2528
+
2529
+// an object to keep initilization configuration
2530
+// needed due to the async nature of the XSS modal
2531
+var initializeConfig = {
2532
+ url: null,
2533
+ custom_info: true,
2534
+};
2535
+
2536
+function loadCustomDashboardInfo(url, callback) {
2537
+ loadJs(url, function () {
2538
+ $.extend(true, netdataDashboard, customDashboard);
2539
+ callback();
2540
+ });
2541
+}
2542
+
2543
+function initializeChartsAndCustomInfo() {
2544
+ NETDATA.alarms.callback = alarmsCallback;
2545
+
2546
+ // download all the charts the server knows
2547
+ NETDATA.chartRegistry.downloadAll(initializeConfig.url, function (data) {
2548
+ if (data !== null) {
2549
+ if (initializeConfig.custom_info === true && typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) {
2550
+ //console.log('loading custom dashboard decorations from server ' + initializeConfig.url);
2551
+ loadCustomDashboardInfo(NETDATA.serverDefault + data.custom_info, function () {
2552
+ initializeDynamicDashboardWithData(data);
2553
+ });
2554
+ }
2555
+ else {
2556
+ //console.log('not loading custom dashboard decorations from server ' + initializeConfig.url);
2557
+ initializeDynamicDashboardWithData(data);
2558
+ }
2559
+ }
2560
+ });
2561
+}
2562
+
2563
+function xssModalDisableXss() {
2564
+ //console.log('disabling xss checks');
2565
+ NETDATA.xss.enabled = false;
2566
+ NETDATA.xss.enabled_for_data = false;
2567
+ initializeConfig.custom_info = true;
2568
+ initializeChartsAndCustomInfo();
2569
+ return false;
2570
+}
2571
+
2572
+function xssModalKeepXss() {
2573
+ //console.log('keeping xss checks');
2574
+ NETDATA.xss.enabled = true;
2575
+ NETDATA.xss.enabled_for_data = true;
2576
+ initializeConfig.custom_info = false;
2577
+ initializeChartsAndCustomInfo();
2578
+ return false;
2579
+}
2580
+
2581
+function initializeDynamicDashboard(netdata_url) {
2582
+ if (typeof netdata_url === 'undefined' || netdata_url === null) {
2583
+ netdata_url = NETDATA.serverDefault;
2584
+ }
2585
+
2586
+ initializeConfig.url = netdata_url;
2587
+
2588
+ // initialize clickable alarms
2589
+ NETDATA.alarms.chart_div_offset = -50;
2590
+ NETDATA.alarms.chart_div_id_prefix = 'chart_';
2591
+ NETDATA.alarms.chart_div_animation_duration = 0;
2592
+
2593
+ NETDATA.pause(function () {
2594
+ if (typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true) {
2595
+ //$("#loadOverlay").css("display","none");
2596
+ document.getElementById('netdataXssModalServer').innerText = initializeConfig.url;
2597
+ $('#xssModal').modal('show');
2598
+ }
2599
+ else {
2600
+ initializeChartsAndCustomInfo();
2601
+ }
2602
+ });
2603
+}
2604
+
2605
+// ----------------------------------------------------------------------------
2606
+
2607
+function versionLog(msg) {
2608
+ document.getElementById('versionCheckLog').innerHTML = msg;
2609
+}
2610
+
2611
+function getNetdataCommitIdFromVersion() {
2612
+ var s = options.version.split('-');
2613
+
2614
+ if (s.length !== 3) {
2615
+ return null;
2616
+ }
2617
+ if (s[2][0] === 'g') {
2618
+ var v = s[2].split('_')[0].substring(1, 8);
2619
+ if (v.length === 7) {
2620
+ versionLog('Installed git commit id of netdata is ' + v);
2621
+ document.getElementById('netdataCommitId').innerHTML = v;
2622
+ return v;
2623
+ }
2624
+ }
2625
+ return null;
2626
+}
2627
+
2628
+function getNetdataCommitId(force, callback) {
2629
+ versionLog('Downloading installed git commit id from netdata...');
2630
+
2631
+ $.ajax({
2632
+ url: 'version.txt',
2633
+ async: true,
2634
+ cache: false,
2635
+ xhrFields: {withCredentials: true} // required for the cookie
2636
+ })
2637
+ .done(function (data) {
2638
+ data = data.replace(/(\r\n|\n|\r| |\t)/gm, "");
2639
+
2640
+ var c = getNetdataCommitIdFromVersion();
2641
+ if (c !== null && data.length === 40 && data.substring(0, 7) !== c) {
2642
+ versionLog('Installed files commit id and internal netdata git commit id do not match');
2643
+ data = c;
2644
+ }
2645
+
2646
+ if (data.length >= 7) {
2647
+ versionLog('Installed git commit id of netdata is ' + data);
2648
+ document.getElementById('netdataCommitId').innerHTML = data.substring(0, 7);
2649
+ callback(data);
2650
+ }
2651
+ })
2652
+ .fail(function () {
2653
+ versionLog('Failed to download installed git commit id from netdata!');
2654
+
2655
+ if (force === true) {
2656
+ var c = getNetdataCommitIdFromVersion();
2657
+ if (c === null) {
2658
+ versionLog('Cannot find the git commit id of netdata.');
2659
+ }
2660
+ callback(c);
2661
+ }
2662
+ else {
2663
+ callback(null);
2664
+ }
2665
+ });
2666
+}
2667
+
2668
+function getGithubLatestCommit(callback) {
2669
+ versionLog('Downloading latest git commit id info from github...');
2670
+
2671
+ $.ajax({
2672
+ url: 'https://api.github.com/repos/netdata/netdata/commits',
2673
+ async: true,
2674
+ cache: false
2675
+ })
2676
+ .done(function (data) {
2677
+ versionLog('Latest git commit id from github is ' + data[0].sha);
2678
+ callback(data[0].sha);
2679
+ })
2680
+ .fail(function () {
2681
+ versionLog('Failed to download installed git commit id from github!');
2682
+ callback(null);
2683
+ });
2684
+}
2685
+
2686
+function checkForUpdate(force, callback) {
2687
+ getNetdataCommitId(force, function (sha1) {
2688
+ if (sha1 === null) {
2689
+ callback(null, null);
2690
+ }
2691
+
2692
+ getGithubLatestCommit(function (sha2) {
2693
+ callback(sha1, sha2);
2694
+ });
2695
+ });
2696
+
2697
+ return null;
2698
+}
2699
+
2700
+function notifyForUpdate(force) {
2701
+ versionLog('<p>checking for updates...</p>');
2702
+
2703
+ var now = Date.now();
2704
+
2705
+ if (typeof force === 'undefined' || force !== true) {
2706
+ var last = loadLocalStorage('last_update_check');
2707
+
2708
+ if (typeof last === 'string') {
2709
+ last = parseInt(last);
2710
+ } else {
2711
+ last = 0;
2712
+ }
2713
+
2714
+ if (now - last < 3600000 * 8) {
2715
+ // no need to check it - too soon
2716
+ return;
2717
+ }
2718
+ }
2719
+
2720
+ checkForUpdate(force, function (sha1, sha2) {
2721
+ var save = false;
2722
+
2723
+ if (sha1 === null) {
2724
+ save = false;
2725
+ versionLog('<p><big>Failed to get your netdata git commit id!</big></p><p>You can always get the latest netdata from <a href="https://github.com/netdata/netdata" target="_blank">its github page</a>.</p>');
2726
+ }
2727
+ else if (sha2 === null) {
2728
+ save = false;
2729
+ versionLog('<p><big>Failed to get the latest git commit id from github.</big></p><p>You can always get the latest netdata from <a href="https://github.com/netdata/netdata" target="_blank">its github page</a>.</p>');
2730
+ }
2731
+ else if (sha1 === sha2) {
2732
+ save = true;
2733
+ versionLog('<p><big>You already have the latest netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/netdata/netdata" target="_blank">give netdata a <b><i class="fas fa-star"></i></b> at its github page</a>.</p>');
2734
+ }
2735
+ else {
2736
+ save = true;
2737
+ var compare = 'https://github.com/netdata/netdata/compare/' + sha1.toString() + '...' + sha2.toString();
2738
+
2739
+ versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest commit: <b><code>' + sha2.substring(0, 7).toString() + '</code></b></p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/netdata/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>');
2740
+
2741
+ document.getElementById('update_badge').innerHTML = '!';
2742
+ }
2743
+
2744
+ if (save) {
2745
+ saveLocalStorage('last_update_check', now.toString());
2746
+ }
2747
+ });
2748
+}
2749
+
2750
+// ----------------------------------------------------------------------------
2751
+// printing dashboards
2752
+
2753
+function showPageFooter() {
2754
+ document.getElementById('footer').style.display = 'block';
2755
+}
2756
+
2757
+function printPreflight() {
2758
+ var url = document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString() + urlOptions.genHash() + ';mode=print';
2759
+ var width = 990;
2760
+ var height = screen.height * 90 / 100;
2761
+ //console.log(url);
2762
+ //console.log(document.location);
2763
+ window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes');
2764
+ $('#printPreflightModal').modal('hide');
2765
+}
2766
+
2767
+function printPage() {
2768
+ var print_is_rendering = true;
2769
+
2770
+ $('#printModal').on('hide.bs.modal', function (e) {
2771
+ if (print_is_rendering === true) {
2772
+ e.preventDefault();
2773
+ return false;
2774
+ }
2775
+
2776
+ return true;
2777
+ });
2778
+
2779
+ $('#printModal').on('show.bs.modal', function () {
2780
+ var print_options = {
2781
+ stop_updates_when_focus_is_lost: false,
2782
+ update_only_visible: false,
2783
+ sync_selection: false,
2784
+ eliminate_zero_dimensions: false,
2785
+ pan_and_zoom_data_padding: false,
2786
+ show_help: false,
2787
+ legend_toolbox: false,
2788
+ resize_charts: false,
2789
+ pixels_per_point: 1
2790
+ };
2791
+
2792
+ var x;
2793
+ for (x in print_options) {
2794
+ if (print_options.hasOwnProperty(x)) {
2795
+ NETDATA.options.current[x] = print_options[x];
2796
+ }
2797
+ }
2798
+
2799
+ NETDATA.parseDom();
2800
+ showPageFooter();
2801
+
2802
+ NETDATA.globalSelectionSync.stop();
2803
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
2804
+ // NETDATA.onresize();
2805
+
2806
+ var el = document.getElementById('printModalProgressBar');
2807
+ var eltxt = document.getElementById('printModalProgressBarText');
2808
+
2809
+ function update_chart(idx) {
2810
+ var state = NETDATA.options.targets[--idx];
2811
+
2812
+ var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length;
2813
+ $(el).css('width', pcent + '%').attr('aria-valuenow', pcent);
2814
+ eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id;
2815
+
2816
+ setTimeout(function () {
2817
+ state.updateChart(function () {
2818
+ NETDATA.options.targets[idx].resizeForPrint();
2819
+
2820
+ if (idx > 0) {
2821
+ update_chart(idx);
2822
+ }
2823
+ else {
2824
+ print_is_rendering = false;
2825
+ $('#printModal').modal('hide');
2826
+ window.print();
2827
+ window.close();
2828
+ }
2829
+ })
2830
+ }, 0);
2831
+ }
2832
+
2833
+ print_is_rendering = true;
2834
+ update_chart(NETDATA.options.targets.length);
2835
+ });
2836
+
2837
+ $('#printModal').modal('show');
2838
+}
2839
+
2840
+// --------------------------------------------------------------------
2841
+
2842
+function jsonStringifyFn(obj) {
2843
+ return JSON.stringify(obj, function (key, value) {
2844
+ return (typeof value === 'function') ? value.toString() : value;
2845
+ });
2846
+}
2847
+
2848
+function jsonParseFn(str) {
2849
+ return JSON.parse(str, function (key, value) {
2850
+ if (typeof value != 'string') {
2851
+ return value;
2852
+ }
2853
+ return (value.substring(0, 8) == 'function') ? eval('(' + value + ')') : value;
2854
+ });
2855
+}
2856
+
2857
+// --------------------------------------------------------------------
2858
+
2859
+var snapshotOptions = {
2860
+ bytes_per_chart: 2048,
2861
+ compressionDefault: 'pako.deflate.base64',
2862
+
2863
+ compressions: {
2864
+ 'none': {
2865
+ bytes_per_point_memory: 5.2,
2866
+ bytes_per_point_disk: 5.6,
2867
+
2868
+ compress: function (s) {
2869
+ return s;
2870
+ },
2871
+
2872
+ compressed_length: function (s) {
2873
+ return s.length;
2874
+ },
2875
+
2876
+ uncompress: function (s) {
2877
+ return s;
2878
+ }
2879
+ },
2880
+
2881
+ 'pako.deflate.base64': {
2882
+ bytes_per_point_memory: 1.8,
2883
+ bytes_per_point_disk: 1.9,
2884
+
2885
+ compress: function (s) {
2886
+ return btoa(pako.deflate(s, {to: 'string'}));
2887
+ },
2888
+
2889
+ compressed_length: function (s) {
2890
+ return s.length;
2891
+ },
2892
+
2893
+ uncompress: function (s) {
2894
+ return pako.inflate(atob(s), {to: 'string'});
2895
+ }
2896
+ },
2897
+
2898
+ 'pako.deflate': {
2899
+ bytes_per_point_memory: 1.4,
2900
+ bytes_per_point_disk: 3.2,
2901
+
2902
+ compress: function (s) {
2903
+ return pako.deflate(s, {to: 'string'});
2904
+ },
2905
+
2906
+ compressed_length: function (s) {
2907
+ return s.length;
2908
+ },
2909
+
2910
+ uncompress: function (s) {
2911
+ return pako.inflate(s, {to: 'string'});
2912
+ }
2913
+ },
2914
+
2915
+ 'lzstring.utf16': {
2916
+ bytes_per_point_memory: 1.7,
2917
+ bytes_per_point_disk: 2.6,
2918
+
2919
+ compress: function (s) {
2920
+ return LZString.compressToUTF16(s);
2921
+ },
2922
+
2923
+ compressed_length: function (s) {
2924
+ return s.length * 2;
2925
+ },
2926
+
2927
+ uncompress: function (s) {
2928
+ return LZString.decompressFromUTF16(s);
2929
+ }
2930
+ },
2931
+
2932
+ 'lzstring.base64': {
2933
+ bytes_per_point_memory: 2.1,
2934
+ bytes_per_point_disk: 2.3,
2935
+
2936
+ compress: function (s) {
2937
+ return LZString.compressToBase64(s);
2938
+ },
2939
+
2940
+ compressed_length: function (s) {
2941
+ return s.length;
2942
+ },
2943
+
2944
+ uncompress: function (s) {
2945
+ return LZString.decompressFromBase64(s);
2946
+ }
2947
+ },
2948
+
2949
+ 'lzstring.uri': {
2950
+ bytes_per_point_memory: 2.1,
2951
+ bytes_per_point_disk: 2.3,
2952
+
2953
+ compress: function (s) {
2954
+ return LZString.compressToEncodedURIComponent(s);
2955
+ },
2956
+
2957
+ compressed_length: function (s) {
2958
+ return s.length;
2959
+ },
2960
+
2961
+ uncompress: function (s) {
2962
+ return LZString.decompressFromEncodedURIComponent(s);
2963
+ }
2964
+ }
2965
+ }
2966
+};
2967
+
2968
+// --------------------------------------------------------------------
2969
+// loading snapshots
2970
+
2971
+function loadSnapshotModalLog(priority, msg) {
2972
+ document.getElementById('loadSnapshotStatus').className = "alert alert-" + priority;
2973
+ document.getElementById('loadSnapshotStatus').innerHTML = msg;
2974
+}
2975
+
2976
+var tmpSnapshotData = null;
2977
+
2978
+function loadSnapshot() {
2979
+ $('#loadSnapshotImport').addClass('disabled');
2980
+
2981
+ if (tmpSnapshotData === null) {
2982
+ loadSnapshotPreflightEmpty();
2983
+ loadSnapshotModalLog('danger', 'no data have been loaded');
2984
+ return;
2985
+ }
2986
+
2987
+ loadPako(function () {
2988
+ loadLzString(function () {
2989
+ loadSnapshotModalLog('info', 'Please wait, activating snapshot...');
2990
+ $('#loadSnapshotModal').modal('hide');
2991
+
2992
+ netdataShowAlarms = false;
2993
+ netdataRegistry = false;
2994
+ netdataServer = tmpSnapshotData.server;
2995
+ NETDATA.serverDefault = netdataServer;
2996
+
2997
+ document.getElementById('charts_div').innerHTML = '';
2998
+ document.getElementById('sidebar').innerHTML = '';
2999
+ NETDATA.globalReset();
3000
+
3001
+ if (typeof tmpSnapshotData.hash !== 'undefined') {
3002
+ urlOptions.hash = tmpSnapshotData.hash;
3003
+ } else {
3004
+ urlOptions.hash = '#';
3005
+ }
3006
+
3007
+ if (typeof tmpSnapshotData.info !== 'undefined') {
3008
+ var info = jsonParseFn(tmpSnapshotData.info);
3009
+ if (typeof info.menu !== 'undefined') {
3010
+ netdataDashboard.menu = info.menu;
3011
+ }
3012
+
3013
+ if (typeof info.submenu !== 'undefined') {
3014
+ netdataDashboard.submenu = info.submenu;
3015
+ }
3016
+
3017
+ if (typeof info.context !== 'undefined') {
3018
+ netdataDashboard.context = info.context;
3019
+ }
3020
+ }
3021
+
3022
+ if (typeof tmpSnapshotData.compression !== 'string') {
3023
+ tmpSnapshotData.compression = 'none';
3024
+ }
3025
+
3026
+ if (typeof snapshotOptions.compressions[tmpSnapshotData.compression] === 'undefined') {
3027
+ alert('unknown compression method: ' + tmpSnapshotData.compression);
3028
+ tmpSnapshotData.compression = 'none';
3029
+ }
3030
+
3031
+ tmpSnapshotData.uncompress = snapshotOptions.compressions[tmpSnapshotData.compression].uncompress;
3032
+ netdataSnapshotData = tmpSnapshotData;
3033
+
3034
+ urlOptions.after = tmpSnapshotData.after_ms;
3035
+ urlOptions.before = tmpSnapshotData.before_ms;
3036
+
3037
+ if (typeof tmpSnapshotData.highlight_after_ms !== 'undefined'
3038
+ && tmpSnapshotData.highlight_after_ms !== null
3039
+ && tmpSnapshotData.highlight_after_ms > 0
3040
+ && typeof tmpSnapshotData.highlight_before_ms !== 'undefined'
3041
+ && tmpSnapshotData.highlight_before_ms !== null
3042
+ && tmpSnapshotData.highlight_before_ms > 0
3043
+ ) {
3044
+ urlOptions.highlight_after = tmpSnapshotData.highlight_after_ms;
3045
+ urlOptions.highlight_before = tmpSnapshotData.highlight_before_ms;
3046
+ urlOptions.highlight = true;
3047
+ }
3048
+ else {
3049
+ urlOptions.highlight_after = 0;
3050
+ urlOptions.highlight_before = 0;
3051
+ urlOptions.highlight = false;
3052
+ }
3053
+
3054
+ netdataCheckXSS = false; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded
3055
+ NETDATA.xss.enabled = true; // we should not do any remote requests, but if we do, check them
3056
+ NETDATA.xss.enabled_for_data = true; // check also snapshot data - that have been excluded from the initial check, due to compression
3057
+ loadSnapshotPreflightEmpty();
3058
+ initializeDynamicDashboard();
3059
+ });
3060
+ });
3061
+};
3062
+
3063
+function loadSnapshotPreflightFile(file) {
3064
+ var filename = NETDATA.xss.string(file.name);
3065
+ var fr = new FileReader();
3066
+ fr.onload = function (e) {
3067
+ document.getElementById('loadSnapshotFilename').innerHTML = filename;
3068
+ var result = null;
3069
+ try {
3070
+ result = NETDATA.xss.checkAlways('snapshot', JSON.parse(e.target.result), /^(snapshot\.info|snapshot\.data)$/);
3071
+
3072
+ //console.log(result);
3073
+ var date_after = new Date(result.after_ms);
3074
+ var date_before = new Date(result.before_ms);
3075
+
3076
+ if (typeof result.charts_ok === 'undefined') {
3077
+ result.charts_ok = 'unknown';
3078
+ }
3079
+
3080
+ if (typeof result.charts_failed === 'undefined') {
3081
+ result.charts_failed = 0;
3082
+ }
3083
+
3084
+ if (typeof result.compression === 'undefined') {
3085
+ result.compression = 'none';
3086
+ }
3087
+
3088
+ if (typeof result.data_size === 'undefined') {
3089
+ result.data_size = 0;
3090
+ }
3091
+
3092
+ document.getElementById('loadSnapshotFilename').innerHTML = '<code>' + filename + '</code>';
3093
+ document.getElementById('loadSnapshotHostname').innerHTML = '<b>' + result.hostname + '</b>, netdata version: <b>' + result.netdata_version.toString() + '</b>';
3094
+ document.getElementById('loadSnapshotURL').innerHTML = result.url;
3095
+ document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point';
3096
+ document.getElementById('loadSnapshotInfo').innerHTML = 'version: <b>' + result.snapshot_version.toString() + '</b>, includes <b>' + result.charts_ok.toString() + '</b> unique chart data queries ' + ((result.charts_failed > 0) ? ('<b>' + result.charts_failed.toString() + '</b> failed') : '').toString() + ', compressed with <code>' + result.compression.toString() + '</code>, data size ' + (Math.round(result.data_size * 100 / 1024 / 1024) / 100).toString() + ' MB';
3097
+ document.getElementById('loadSnapshotTimeRange').innerHTML = '<b>' + NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + '</b> to <b>' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before) + '</b>';
3098
+ document.getElementById('loadSnapshotComments').innerHTML = ((result.comments) ? result.comments : '').toString();
3099
+ loadSnapshotModalLog('success', 'File loaded, click <b>Import</b> to render it!');
3100
+ $('#loadSnapshotImport').removeClass('disabled');
3101
+
3102
+ tmpSnapshotData = result;
3103
+ }
3104
+ catch (e) {
3105
+ console.log(e);
3106
+ document.getElementById('loadSnapshotStatus').className = "alert alert-danger";
3107
+ document.getElementById('loadSnapshotStatus').innerHTML = "Failed to parse this file!";
3108
+ $('#loadSnapshotImport').addClass('disabled');
3109
+ }
3110
+ }
3111
+
3112
+ //console.log(file);
3113
+ fr.readAsText(file);
3114
+};
3115
+
3116
+function loadSnapshotPreflightEmpty() {
3117
+ document.getElementById('loadSnapshotFilename').innerHTML = '';
3118
+ document.getElementById('loadSnapshotHostname').innerHTML = '';
3119
+ document.getElementById('loadSnapshotURL').innerHTML = '';
3120
+ document.getElementById('loadSnapshotCharts').innerHTML = '';
3121
+ document.getElementById('loadSnapshotInfo').innerHTML = '';
3122
+ document.getElementById('loadSnapshotTimeRange').innerHTML = '';
3123
+ document.getElementById('loadSnapshotComments').innerHTML = '';
3124
+ loadSnapshotModalLog('success', 'Browse for a snapshot file (or drag it and drop it here), then click <b>Import</b> to render it.');
3125
+ $('#loadSnapshotImport').addClass('disabled');
3126
+};
3127
+
3128
+var loadSnapshotDragAndDropInitialized = false;
3129
+
3130
+function loadSnapshotDragAndDropSetup() {
3131
+ if (loadSnapshotDragAndDropInitialized === false) {
3132
+ loadSnapshotDragAndDropInitialized = true;
3133
+ $('#loadSnapshotDragAndDrop')
3134
+ .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) {
3135
+ e.preventDefault();
3136
+ e.stopPropagation();
3137
+ })
3138
+ .on('drop', function (e) {
3139
+ if (e.originalEvent.dataTransfer.files.length) {
3140
+ loadSnapshotPreflightFile(e.originalEvent.dataTransfer.files.item(0));
3141
+ }
3142
+ else {
3143
+ loadSnapshotPreflightEmpty();
3144
+ loadSnapshotModalLog('danger', 'No file selected');
3145
+ }
3146
+ });
3147
+ }
3148
+};
3149
+
3150
+function loadSnapshotPreflight() {
3151
+ var files = document.getElementById('loadSnapshotSelectFiles').files;
3152
+ if (files.length <= 0) {
3153
+ loadSnapshotPreflightEmpty();
3154
+ loadSnapshotModalLog('danger', 'No file selected');
3155
+ return;
3156
+ }
3157
+
3158
+ loadSnapshotModalLog('info', 'Loading file...');
3159
+
3160
+ loadSnapshotPreflightFile(files.item(0));
3161
+}
3162
+
3163
+// --------------------------------------------------------------------
3164
+// saving snapshots
3165
+
3166
+var saveSnapshotStop = false;
3167
+
3168
+function saveSnapshotCancel() {
3169
+ saveSnapshotStop = true;
3170
+}
3171
+
3172
+var saveSnapshotModalInitialized = false;
3173
+
3174
+function saveSnapshotModalSetup() {
3175
+ if (saveSnapshotModalInitialized === false) {
3176
+ saveSnapshotModalInitialized = true;
3177
+ $('#saveSnapshotModal')
3178
+ .on('hide.bs.modal', saveSnapshotCancel)
3179
+ .on('show.bs.modal', saveSnapshotModalInit)
3180
+ .on('shown.bs.modal', function () {
3181
+ $('#saveSnapshotResolutionSlider').find(".slider-handle:first").attr("tabindex", 1);
3182
+ document.getElementById('saveSnapshotComments').focus();
3183
+ });
3184
+ }
3185
+};
3186
+
3187
+function saveSnapshotModalLog(priority, msg) {
3188
+ document.getElementById('saveSnapshotStatus').className = "alert alert-" + priority;
3189
+ document.getElementById('saveSnapshotStatus').innerHTML = msg;
3190
+}
3191
+
3192
+function saveSnapshotModalShowExpectedSize() {
3193
+ var points = Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint);
3194
+ var priority = 'info';
3195
+ var msg = 'A moderate snapshot.';
3196
+
3197
+ var sizemb = Math.round(
3198
+ (options.data.charts_count * snapshotOptions.bytes_per_chart
3199
+ + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_disk)
3200
+ * 10 / 1024 / 1024) / 10;
3201
+
3202
+ var memmb = Math.round(
3203
+ (options.data.charts_count * snapshotOptions.bytes_per_chart
3204
+ + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_memory)
3205
+ * 10 / 1024 / 1024) / 10;
3206
+
3207
+ if (sizemb < 10) {
3208
+ priority = 'success';
3209
+ msg = 'A nice small snapshot!';
3210
+ }
3211
+ if (sizemb > 50) {
3212
+ priority = 'warning';
3213
+ msg = 'Will stress your browser...';
3214
+ }
3215
+ if (sizemb > 100) {
3216
+ priority = 'danger';
3217
+ msg = 'Hm... good luck...';
3218
+ }
3219
+
3220
+ saveSnapshotModalLog(priority, 'The snapshot will have ' + points.toString() + ' points per dimension. Expected size on disk ' + sizemb + ' MB, at browser memory ' + memmb + ' MB.<br/>' + msg);
3221
+}
3222
+
3223
+var saveSnapshotCompression = snapshotOptions.compressionDefault;
3224
+
3225
+function saveSnapshotSetCompression(name) {
3226
+ saveSnapshotCompression = name;
3227
+ document.getElementById('saveSnapshotCompressionName').innerHTML = saveSnapshotCompression;
3228
+ saveSnapshotModalShowExpectedSize();
3229
+}
3230
+
3231
+var saveSnapshotSlider = null;
3232
+var saveSnapshotSelectedSecondsPerPoint = 1;
3233
+var saveSnapshotViewDuration = 1;
3234
+
3235
+function saveSnapshotModalInit() {
3236
+ $('#saveSnapshotModalProgressSection').hide();
3237
+ $('#saveSnapshotResolutionRadio').show();
3238
+ saveSnapshotModalLog('info', 'Select resolution and click <b>Save</b>');
3239
+ $('#saveSnapshotExport').removeClass('disabled');
3240
+
3241
+ loadBootstrapSlider(function () {
3242
+ saveSnapshotViewDuration = options.duration;
3243
+ var start_ms = Math.round(Date.now() - saveSnapshotViewDuration * 1000);
3244
+
3245
+ if (NETDATA.globalPanAndZoom.isActive() === true) {
3246
+ saveSnapshotViewDuration = Math.round((NETDATA.globalPanAndZoom.force_before_ms - NETDATA.globalPanAndZoom.force_after_ms) / 1000);
3247
+ start_ms = NETDATA.globalPanAndZoom.force_after_ms;
3248
+ }
3249
+
3250
+ var start_date = new Date(start_ms);
3251
+ var yyyymmddhhssmm = start_date.getFullYear() + NETDATA.zeropad(start_date.getMonth() + 1) + NETDATA.zeropad(start_date.getDate()) + '-' + NETDATA.zeropad(start_date.getHours()) + NETDATA.zeropad(start_date.getMinutes()) + NETDATA.zeropad(start_date.getSeconds());
3252
+
3253
+ document.getElementById('saveSnapshotFilename').value = 'netdata-' + options.hostname.toString() + '-' + yyyymmddhhssmm.toString() + '-' + saveSnapshotViewDuration.toString() + '.snapshot';
3254
+ saveSnapshotSetCompression(saveSnapshotCompression);
3255
+
3256
+ var min = options.update_every;
3257
+ var max = Math.round(saveSnapshotViewDuration / 100);
3258
+
3259
+ if (NETDATA.globalPanAndZoom.isActive() === false) {
3260
+ max = Math.round(saveSnapshotViewDuration / 50);
3261
+ }
3262
+
3263
+ var view = Math.round(saveSnapshotViewDuration / Math.round($(document.getElementById('charts_div')).width() / 2));
3264
+
3265
+ // console.log('view duration: ' + saveSnapshotViewDuration + ', min: ' + min + ', max: ' + max + ', view: ' + view);
3266
+
3267
+ if (max < 10) {
3268
+ max = 10;
3269
+ }
3270
+ if (max < min) {
3271
+ max = min;
3272
+ }
3273
+ if (view < min) {
3274
+ view = min;
3275
+ }
3276
+ if (view > max) {
3277
+ view = max;
3278
+ }
3279
+
3280
+ if (saveSnapshotSlider !== null) {
3281
+ saveSnapshotSlider.destroy();
3282
+ }
3283
+
3284
+ saveSnapshotSlider = new Slider('#saveSnapshotResolutionSlider', {
3285
+ ticks: [min, view, max],
3286
+ min: min,
3287
+ max: max,
3288
+ step: options.update_every,
3289
+ value: view,
3290
+ scale: (max > 100) ? 'logarithmic' : 'linear',
3291
+ tooltip: 'always',
3292
+ formatter: function (value) {
3293
+ if (value < 1) {
3294
+ value = 1;
3295
+ }
3296
+
3297
+ if (value < options.data.update_every) {
3298
+ value = options.data.update_every;
3299
+ }
3300
+
3301
+ saveSnapshotSelectedSecondsPerPoint = value;
3302
+ saveSnapshotModalShowExpectedSize();
3303
+
3304
+ var seconds = ' seconds ';
3305
+ if (value === 1) {
3306
+ seconds = ' second ';
3307
+ }
3308
+
3309
+ return value + seconds + 'per point' + ((value === options.data.update_every) ? ', server default' : '').toString();
3310
+ }
3311
+ });
3312
+ });
3313
+}
3314
+
3315
+function saveSnapshot() {
3316
+ loadPako(function () {
3317
+ loadLzString(function () {
3318
+ saveSnapshotStop = false;
3319
+ $('#saveSnapshotModalProgressSection').show();
3320
+ $('#saveSnapshotResolutionRadio').hide();
3321
+ $('#saveSnapshotExport').addClass('disabled');
3322
+
3323
+ var filename = document.getElementById('saveSnapshotFilename').value;
3324
+ // console.log(filename);
3325
+ saveSnapshotModalLog('info', 'Generating snapshot as <code>' + filename.toString() + '</code>');
3326
+
3327
+ var save_options = {
3328
+ stop_updates_when_focus_is_lost: false,
3329
+ update_only_visible: false,
3330
+ sync_selection: false,
3331
+ eliminate_zero_dimensions: true,
3332
+ pan_and_zoom_data_padding: false,
3333
+ show_help: false,
3334
+ legend_toolbox: false,
3335
+ resize_charts: false,
3336
+ pixels_per_point: 1
3337
+ };
3338
+ var backedup_options = {};
3339
+
3340
+ var x;
3341
+ for (x in save_options) {
3342
+ if (save_options.hasOwnProperty(x)) {
3343
+ backedup_options[x] = NETDATA.options.current[x];
3344
+ NETDATA.options.current[x] = save_options[x];
3345
+ }
3346
+ }
3347
+
3348
+ var el = document.getElementById('saveSnapshotModalProgressBar');
3349
+ var eltxt = document.getElementById('saveSnapshotModalProgressBarText');
3350
+
3351
+ options.data.charts_by_name = null;
3352
+
3353
+ var saveData = {
3354
+ hostname: options.hostname,
3355
+ server: NETDATA.serverDefault,
3356
+ netdata_version: options.data.version,
3357
+ snapshot_version: 1,
3358
+ after_ms: Date.now() - options.duration * 1000,
3359
+ before_ms: Date.now(),
3360
+ highlight_after_ms: urlOptions.highlight_after,
3361
+ highlight_before_ms: urlOptions.highlight_before,
3362
+ duration_ms: options.duration * 1000,
3363
+ update_every_ms: options.update_every * 1000,
3364
+ data_points: 0,
3365
+ url: ((urlOptions.server !== null) ? urlOptions.server : document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString()).toString(),
3366
+ comments: document.getElementById('saveSnapshotComments').value.toString(),
3367
+ hash: urlOptions.hash,
3368
+ charts: options.data,
3369
+ info: jsonStringifyFn({
3370
+ menu: netdataDashboard.menu,
3371
+ submenu: netdataDashboard.submenu,
3372
+ context: netdataDashboard.context
3373
+ }),
3374
+ charts_ok: 0,
3375
+ charts_failed: 0,
3376
+ compression: saveSnapshotCompression,
3377
+ data_size: 0,
3378
+ data: {}
3379
+ };
3380
+
3381
+ if (typeof snapshotOptions.compressions[saveData.compression] === 'undefined') {
3382
+ alert('unknown compression method: ' + saveData.compression);
3383
+ saveData.compression = 'none';
3384
+ }
3385
+
3386
+ var compress = snapshotOptions.compressions[saveData.compression].compress;
3387
+ var compressed_length = snapshotOptions.compressions[saveData.compression].compressed_length;
3388
+
3389
+ function pack_api1_v1_chart_data(state) {
3390
+ if (state.library_name === null || state.data === null) {
3391
+ return;
3392
+ }
3393
+
3394
+ var data = state.data;
3395
+ state.data = null;
3396
+ data.state = null;
3397
+ var str = JSON.stringify(data);
3398
+
3399
+ if (typeof str === 'string') {
3400
+ var cstr = compress(str);
3401
+ saveData.data[state.chartDataUniqueID()] = cstr;
3402
+ return compressed_length(cstr);
3403
+ }
3404
+ else {
3405
+ return 0;
3406
+ }
3407
+ }
3408
+
3409
+ var clearPanAndZoom = false;
3410
+ if (NETDATA.globalPanAndZoom.isActive() === false) {
3411
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveData.after_ms, saveData.before_ms);
3412
+ clearPanAndZoom = true;
3413
+ }
3414
+
3415
+ saveData.after_ms = NETDATA.globalPanAndZoom.force_after_ms;
3416
+ saveData.before_ms = NETDATA.globalPanAndZoom.force_before_ms;
3417
+ saveData.duration_ms = saveData.before_ms - saveData.after_ms;
3418
+ saveData.data_points = Math.round((saveData.before_ms - saveData.after_ms) / (saveSnapshotSelectedSecondsPerPoint * 1000));
3419
+ saveSnapshotModalLog('info', 'Generating snapshot with ' + saveData.data_points.toString() + ' data points per dimension...');
3420
+
3421
+ var charts_count = 0;
3422
+ var charts_ok = 0;
3423
+ var charts_failed = 0;
3424
+
3425
+ function saveSnapshotRestore() {
3426
+ $('#saveSnapshotModal').modal('hide');
3427
+
3428
+ // restore the options
3429
+ var x;
3430
+ for (x in backedup_options) {
3431
+ if (backedup_options.hasOwnProperty(x)) {
3432
+ NETDATA.options.current[x] = backedup_options[x];
3433
+ }
3434
+ }
3435
+
3436
+ $(el).css('width', '0%').attr('aria-valuenow', 0);
3437
+ eltxt.innerText = '0%';
3438
+
3439
+ if (clearPanAndZoom) {
3440
+ NETDATA.globalPanAndZoom.clearMaster();
3441
+ }
3442
+
3443
+ NETDATA.options.force_data_points = 0;
3444
+ NETDATA.options.fake_chart_rendering = false;
3445
+ NETDATA.onscroll_updater_enabled = true;
3446
+ NETDATA.onresize();
3447
+ NETDATA.unpause();
3448
+
3449
+ $('#saveSnapshotExport').removeClass('disabled');
3450
+ }
3451
+
3452
+ NETDATA.globalSelectionSync.stop();
3453
+ NETDATA.options.force_data_points = saveData.data_points;
3454
+ NETDATA.options.fake_chart_rendering = true;
3455
+ NETDATA.onscroll_updater_enabled = false;
3456
+ NETDATA.abortAllRefreshes();
3457
+
3458
+ var size = 0;
3459
+ var info = ' Resolution: <b>' + saveSnapshotSelectedSecondsPerPoint.toString() + ((saveSnapshotSelectedSecondsPerPoint === 1) ? ' second ' : ' seconds ').toString() + 'per point</b>.';
3460
+
3461
+ function update_chart(idx) {
3462
+ if (saveSnapshotStop === true) {
3463
+ saveSnapshotModalLog('info', 'Cancelled!');
3464
+ saveSnapshotRestore();
3465
+ return;
3466
+ }
3467
+
3468
+ var state = NETDATA.options.targets[--idx];
3469
+
3470
+ var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length;
3471
+ $(el).css('width', pcent + '%').attr('aria-valuenow', pcent);
3472
+ eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id;
3473
+
3474
+ setTimeout(function () {
3475
+ charts_count++;
3476
+ state.isVisible(true);
3477
+ state.current.force_after_ms = saveData.after_ms;
3478
+ state.current.force_before_ms = saveData.before_ms;
3479
+
3480
+ state.updateChart(function (status, reason) {
3481
+ state.current.force_after_ms = null;
3482
+ state.current.force_before_ms = null;
3483
+
3484
+ if (status === true) {
3485
+ charts_ok++;
3486
+ // state.log('ok');
3487
+ size += pack_api1_v1_chart_data(state);
3488
+ }
3489
+ else {
3490
+ charts_failed++;
3491
+ state.log('failed to be updated: ' + reason);
3492
+ }
3493
+
3494
+ saveSnapshotModalLog((charts_failed) ? 'danger' : 'info', 'Generated snapshot data size <b>' + (Math.round(size * 100 / 1024 / 1024) / 100).toString() + ' MB</b>. ' + ((charts_failed) ? (charts_failed.toString() + ' charts have failed to be downloaded') : '').toString() + info);
3495
+
3496
+ if (idx > 0) {
3497
+ update_chart(idx);
3498
+ }
3499
+ else {
3500
+ saveData.charts_ok = charts_ok;
3501
+ saveData.charts_failed = charts_failed;
3502
+ saveData.data_size = size;
3503
+ // console.log(saveData.compression + ': ' + (size / (options.data.dimensions_count * Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint))).toString());
3504
+
3505
+ // save it
3506
+ // console.log(saveData);
3507
+ saveObjectToClient(saveData, filename);
3508
+
3509
+ if (charts_failed > 0) {
3510
+ alert(charts_failed.toString() + ' failed to be downloaded');
3511
+ }
3512
+
3513
+ saveSnapshotRestore();
3514
+ saveData = null;
3515
+ }
3516
+ })
3517
+ }, 0);
3518
+ }
3519
+
3520
+ update_chart(NETDATA.options.targets.length);
3521
+ });
3522
+ });
3523
+}
3524
+
3525
+// --------------------------------------------------------------------
3526
+// activate netdata on the page
3527
+
3528
+function dashboardSettingsSetup() {
3529
+ var update_options_modal = function () {
3530
+ // console.log('update_options_modal');
3531
+
3532
+ var sync_option = function (option) {
3533
+ var self = $('#' + option);
3534
+
3535
+ if (self.prop('checked') !== NETDATA.getOption(option)) {
3536
+ // console.log('switching ' + option.toString());
3537
+ self.bootstrapToggle(NETDATA.getOption(option) ? 'on' : 'off');
3538
+ }
3539
+ };
3540
+
3541
+ var theme_sync_option = function (option) {
3542
+ var self = $('#' + option);
3543
+
3544
+ self.bootstrapToggle(netdataTheme === 'slate' ? 'on' : 'off');
3545
+ };
3546
+ var units_sync_option = function (option) {
3547
+ var self = $('#' + option);
3548
+
3549
+ if (self.prop('checked') !== (NETDATA.getOption('units') === 'auto')) {
3550
+ self.bootstrapToggle(NETDATA.getOption('units') === 'auto' ? 'on' : 'off');
3551
+ }
3552
+
3553
+ if (self.prop('checked') === true) {
3554
+ $('#settingsLocaleTempRow').show();
3555
+ $('#settingsLocaleTimeRow').show();
3556
+ }
3557
+ else {
3558
+ $('#settingsLocaleTempRow').hide();
3559
+ $('#settingsLocaleTimeRow').hide();
3560
+ }
3561
+ };
3562
+ var temp_sync_option = function (option) {
3563
+ var self = $('#' + option);
3564
+
3565
+ if (self.prop('checked') !== (NETDATA.getOption('temperature') === 'celsius')) {
3566
+ self.bootstrapToggle(NETDATA.getOption('temperature') === 'celsius' ? 'on' : 'off');
3567
+ }
3568
+ };
3569
+ var timezone_sync_option = function (option) {
3570
+ var self = $('#' + option);
3571
+
3572
+ document.getElementById('browser_timezone').innerText = NETDATA.options.browser_timezone;
3573
+ document.getElementById('server_timezone').innerText = NETDATA.options.server_timezone;
3574
+ document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone;
3575
+
3576
+ if (self.prop('checked') === NETDATA.dateTime.using_timezone) {
3577
+ self.bootstrapToggle(NETDATA.dateTime.using_timezone ? 'off' : 'on');
3578
+ }
3579
+ };
3580
+
3581
+ sync_option('eliminate_zero_dimensions');
3582
+ sync_option('destroy_on_hide');
3583
+ sync_option('async_on_scroll');
3584
+ sync_option('parallel_refresher');
3585
+ sync_option('concurrent_refreshes');
3586
+ sync_option('sync_selection');
3587
+ sync_option('sync_pan_and_zoom');
3588
+ sync_option('stop_updates_when_focus_is_lost');
3589
+ sync_option('smooth_plot');
3590
+ sync_option('pan_and_zoom_data_padding');
3591
+ sync_option('show_help');
3592
+ sync_option('seconds_as_time');
3593
+ theme_sync_option('netdata_theme_control');
3594
+ units_sync_option('units_conversion');
3595
+ temp_sync_option('units_temp');
3596
+ timezone_sync_option('local_timezone');
3597
+
3598
+ if (NETDATA.getOption('parallel_refresher') === false) {
3599
+ $('#concurrent_refreshes_row').hide();
3600
+ }
3601
+ else {
3602
+ $('#concurrent_refreshes_row').show();
3603
+ }
3604
+ };
3605
+ NETDATA.setOption('setOptionCallback', update_options_modal);
3606
+
3607
+ // handle options changes
3608
+ $('#eliminate_zero_dimensions').change(function () {
3609
+ NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked'));
3610
+ });
3611
+ $('#destroy_on_hide').change(function () {
3612
+ NETDATA.setOption('destroy_on_hide', $(this).prop('checked'));
3613
+ });
3614
+ $('#async_on_scroll').change(function () {
3615
+ NETDATA.setOption('async_on_scroll', $(this).prop('checked'));
3616
+ });
3617
+ $('#parallel_refresher').change(function () {
3618
+ NETDATA.setOption('parallel_refresher', $(this).prop('checked'));
3619
+ });
3620
+ $('#concurrent_refreshes').change(function () {
3621
+ NETDATA.setOption('concurrent_refreshes', $(this).prop('checked'));
3622
+ });
3623
+ $('#sync_selection').change(function () {
3624
+ NETDATA.setOption('sync_selection', $(this).prop('checked'));
3625
+ });
3626
+ $('#sync_pan_and_zoom').change(function () {
3627
+ NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked'));
3628
+ });
3629
+ $('#stop_updates_when_focus_is_lost').change(function () {
3630
+ urlOptions.update_always = !$(this).prop('checked');
3631
+ urlOptions.hashUpdate();
3632
+
3633
+ NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
3634
+ });
3635
+ $('#smooth_plot').change(function () {
3636
+ NETDATA.setOption('smooth_plot', $(this).prop('checked'));
3637
+ });
3638
+ $('#pan_and_zoom_data_padding').change(function () {
3639
+ NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked'));
3640
+ });
3641
+ $('#seconds_as_time').change(function () {
3642
+ NETDATA.setOption('seconds_as_time', $(this).prop('checked'));
3643
+ });
3644
+ $('#local_timezone').change(function () {
3645
+ if ($(this).prop('checked')) {
3646
+ selected_server_timezone('default', true);
3647
+ } else {
3648
+ selected_server_timezone('default', false);
3649
+ }
3650
+ });
3651
+
3652
+ $('#units_conversion').change(function () {
3653
+ NETDATA.setOption('units', $(this).prop('checked') ? 'auto' : 'original');
3654
+ });
3655
+ $('#units_temp').change(function () {
3656
+ NETDATA.setOption('temperature', $(this).prop('checked') ? 'celsius' : 'fahrenheit');
3657
+ });
3658
+
3659
+ $('#show_help').change(function () {
3660
+ urlOptions.help = $(this).prop('checked');
3661
+ urlOptions.hashUpdate();
3662
+
3663
+ NETDATA.setOption('show_help', urlOptions.help);
3664
+ netdataReload();
3665
+ });
3666
+
3667
+ // this has to be the last
3668
+ // it reloads the page
3669
+ $('#netdata_theme_control').change(function () {
3670
+ urlOptions.theme = $(this).prop('checked') ? 'slate' : 'white';
3671
+ urlOptions.hashUpdate();
3672
+
3673
+ if (setTheme(urlOptions.theme)) {
3674
+ netdataReload();
3675
+ }
3676
+ });
3677
+}
3678
+
3679
+function scrollDashboardTo() {
3680
+ if (netdataSnapshotData !== null && typeof netdataSnapshotData.hash !== 'undefined') {
3681
+ //console.log(netdataSnapshotData.hash);
3682
+ scrollToId(netdataSnapshotData.hash.replace('#', ''));
3683
+ }
3684
+ else {
3685
+ // check if we have to jump to a specific section
3686
+ scrollToId(urlOptions.hash.replace('#', ''));
3687
+
3688
+ if (urlOptions.chart !== null) {
3689
+ NETDATA.alarms.scrollToChart(urlOptions.chart);
3690
+ //urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu);
3691
+ //urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart);
3692
+ //console.log('hash = ' + urlOptions.hash);
3693
+ }
3694
+ }
3695
+}
3696
+
3697
+var modalHiddenCallback = null;
3698
+
3699
+function scrollToChartAfterHidingModal(chart) {
3700
+ modalHiddenCallback = function () {
3701
+ NETDATA.alarms.scrollToChart(chart);
3702
+ };
3703
+}
3704
+
3705
+// ----------------------------------------------------------------------------
3706
+
3707
+function enableTooltipsAndPopovers() {
3708
+ $('[data-toggle="tooltip"]').tooltip({
3709
+ animated: 'fade',
3710
+ trigger: 'hover',
3711
+ html: true,
3712
+ delay: {show: 500, hide: 0},
3713
+ container: 'body'
3714
+ });
3715
+ $('[data-toggle="popover"]').popover();
3716
+}
3717
+
3718
+// ----------------------------------------------------------------------------
3719
+
3720
+var runOnceOnDashboardLastRun = 0;
3721
+
3722
+function runOnceOnDashboardWithjQuery() {
3723
+ if (runOnceOnDashboardLastRun !== 0) {
3724
+ scrollDashboardTo();
3725
+
3726
+ // restore the scrollspy at the proper position
3727
+ $(document.body).scrollspy('refresh');
3728
+ $(document.body).scrollspy('process');
3729
+
3730
+ return;
3731
+ }
3732
+
3733
+ runOnceOnDashboardLastRun = Date.now();
3734
+
3735
+ // ------------------------------------------------------------------------
3736
+ // bootstrap modals
3737
+
3738
+ // prevent bootstrap modals from scrolling the page
3739
+ // maintains the current scroll position
3740
+ // https://stackoverflow.com/a/34754029/4525767
3741
+
3742
+ var scrollPos = 0;
3743
+ var modal_depth = 0; // how many modals are currently open
3744
+ var modal_shown = false; // set to true, if a modal is shown
3745
+ var netdata_paused_on_modal = false; // set to true, if the modal paused netdata
3746
+ var scrollspyOffset = $(window).height() / 3; // will be updated below - the offset of scrollspy to select an item
3747
+
3748
+ $('.modal')
3749
+ .on('show.bs.modal', function () {
3750
+ if (modal_depth === 0) {
3751
+ scrollPos = window.scrollY;
3752
+
3753
+ $('body').css({
3754
+ overflow: 'hidden',
3755
+ position: 'fixed',
3756
+ top: -scrollPos
3757
+ });
3758
+
3759
+ modal_shown = true;
3760
+
3761
+ if (NETDATA.options.pauseCallback === null) {
3762
+ NETDATA.pause(function () {
3763
+ });
3764
+ netdata_paused_on_modal = true;
3765
+ }
3766
+ else {
3767
+ netdata_paused_on_modal = false;
3768
+ }
3769
+ }
3770
+
3771
+ modal_depth++;
3772
+ //console.log(urlOptions.after);
3773
+
3774
+ })
3775
+ .on('hide.bs.modal', function () {
3776
+
3777
+ modal_depth--;
3778
+
3779
+ if (modal_depth <= 0) {
3780
+ modal_depth = 0;
3781
+
3782
+ $('body')
3783
+ .css({
3784
+ overflow: '',
3785
+ position: '',
3786
+ top: ''
3787
+ });
3788
+
3789
+ // scroll to the position we had open before the modal
3790
+ $('html, body')
3791
+ .animate({scrollTop: scrollPos}, 0);
3792
+
3793
+ // unpause netdata, if we paused it
3794
+ if (netdata_paused_on_modal === true) {
3795
+ NETDATA.unpause();
3796
+ netdata_paused_on_modal = false;
3797
+ }
3798
+
3799
+ // restore the scrollspy at the proper position
3800
+ $(document.body).scrollspy('process');
3801
+ }
3802
+ //console.log(urlOptions.after);
3803
+ })
3804
+ .on('hidden.bs.modal', function () {
3805
+ if (modal_depth === 0) {
3806
+ modal_shown = false;
3807
+ }
3808
+
3809
+ if (typeof modalHiddenCallback === 'function') {
3810
+ modalHiddenCallback();
3811
+ }
3812
+
3813
+ modalHiddenCallback = null;
3814
+ //console.log(urlOptions.after);
3815
+ });
3816
+
3817
+ // ------------------------------------------------------------------------
3818
+ // sidebar / affix
3819
+
3820
+ $('#sidebar')
3821
+ .affix({
3822
+ offset: {
3823
+ top: (isdemo()) ? 150 : 0,
3824
+ bottom: 0
3825
+ }
3826
+ })
3827
+ .on('affixed.bs.affix', function () {
3828
+ // fix scrolling of very long affix lists
3829
+ // http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long
3830
+
3831
+ $(this).removeAttr('style');
3832
+ })
3833
+ .on('affix-top.bs.affix', function () {
3834
+ // fix bootstrap affix click bug
3835
+ // https://stackoverflow.com/a/37847981/4525767
3836
+
3837
+ if (modal_shown) {
3838
+ return false;
3839
+ }
3840
+ })
3841
+ .on('activate.bs.scrollspy', function (e) {
3842
+ // change the URL based on the current position of the screen
3843
+
3844
+ if (modal_shown === false) {
3845
+ var el = $(e.target);
3846
+ var hash = el.find('a').attr('href');
3847
+ if (typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) {
3848
+ urlOptions.hash = hash;
3849
+ urlOptions.hashUpdate();
3850
+ }
3851
+ }
3852
+ });
3853
+
3854
+ Ps.initialize(document.getElementById('sidebar'), {
3855
+ wheelSpeed: 0.5,
3856
+ wheelPropagation: true,
3857
+ swipePropagation: true,
3858
+ minScrollbarLength: null,
3859
+ maxScrollbarLength: null,
3860
+ useBothWheelAxes: false,
3861
+ suppressScrollX: true,
3862
+ suppressScrollY: false,
3863
+ scrollXMarginOffset: 0,
3864
+ scrollYMarginOffset: 0,
3865
+ theme: 'default'
3866
+ });
3867
+
3868
+ // ------------------------------------------------------------------------
3869
+ // scrollspy
3870
+
3871
+ if (scrollspyOffset > 250) {
3872
+ scrollspyOffset = 250;
3873
+ }
3874
+ if (scrollspyOffset < 75) {
3875
+ scrollspyOffset = 75;
3876
+ }
3877
+ document.body.setAttribute('data-offset', scrollspyOffset);
3878
+
3879
+ // scroll the dashboard, before activating the scrollspy, so that our
3880
+ // hash will not be updated before we got the chance to scroll to it
3881
+ scrollDashboardTo();
3882
+
3883
+ $(document.body).scrollspy({
3884
+ target: '#sidebar',
3885
+ offset: scrollspyOffset // controls the diff of the <hX> element to the top, to select it
3886
+ });
3887
+
3888
+ // ------------------------------------------------------------------------
3889
+ // my-netdata menu
3890
+
3891
+ Ps.initialize(document.getElementById('myNetdataDropdownUL'), {
3892
+ wheelSpeed: 1,
3893
+ wheelPropagation: false,
3894
+ swipePropagation: false,
3895
+ minScrollbarLength: null,
3896
+ maxScrollbarLength: null,
3897
+ useBothWheelAxes: false,
3898
+ suppressScrollX: true,
3899
+ suppressScrollY: false,
3900
+ scrollXMarginOffset: 0,
3901
+ scrollYMarginOffset: 0,
3902
+ theme: 'default'
3903
+ });
3904
+
3905
+ $('#myNetdataDropdownParent')
3906
+ .on('show.bs.dropdown', function () {
3907
+ var hash = urlOptions.genHash();
3908
+ $('.registry_link').each(function (idx) {
3909
+ this.setAttribute('href', this.getAttribute("href").replace(/#.*$/, hash));
3910
+ });
3911
+
3912
+ NETDATA.pause(function () {
3913
+ });
3914
+ })
3915
+ .on('shown.bs.dropdown', function () {
3916
+ Ps.update(document.getElementById('myNetdataDropdownUL'));
3917
+ })
3918
+ .on('hidden.bs.dropdown', function () {
3919
+ NETDATA.unpause();
3920
+ });
3921
+
3922
+ $('#deleteRegistryModal')
3923
+ .on('hidden.bs.modal', function () {
3924
+ deleteRegistryGuid = null;
3925
+ });
3926
+
3927
+ // ------------------------------------------------------------------------
3928
+ // update modal
3929
+
3930
+ $('#updateModal')
3931
+ .on('show.bs.modal', function () {
3932
+ versionLog('checking, please wait...');
3933
+ })
3934
+ .on('shown.bs.modal', function () {
3935
+ notifyForUpdate(true);
3936
+ });
3937
+
3938
+ // ------------------------------------------------------------------------
3939
+ // alarms modal
3940
+
3941
+ $('#alarmsModal')
3942
+ .on('shown.bs.modal', function () {
3943
+ alarmsUpdateModal();
3944
+ })
3945
+ .on('hidden.bs.modal', function () {
3946
+ document.getElementById('alarms_active').innerHTML =
3947
+ document.getElementById('alarms_all').innerHTML =
3948
+ document.getElementById('alarms_log').innerHTML =
3949
+ 'loading...';
3950
+ });
3951
+
3952
+ // ------------------------------------------------------------------------
3953
+
3954
+ dashboardSettingsSetup();
3955
+ loadSnapshotDragAndDropSetup();
3956
+ saveSnapshotModalSetup();
3957
+ showPageFooter();
3958
+
3959
+ // ------------------------------------------------------------------------
3960
+ // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js
3961
+
3962
+ $.fn.shorten = function (settings) {
3963
+ "use strict";
3964
+
3965
+ var config = {
3966
+ showChars: 750,
3967
+ minHideChars: 10,
3968
+ ellipsesText: "...",
3969
+ moreText: '<i class="fas fa-expand"></i> show more information',
3970
+ lessText: '<i class="fas fa-compress"></i> show less information',
3971
+ onLess: function () {
3972
+ NETDATA.onscroll();
3973
+ },
3974
+ onMore: function () {
3975
+ NETDATA.onscroll();
3976
+ },
3977
+ errMsg: null,
3978
+ force: false
3979
+ };
3980
+
3981
+ if (settings) {
3982
+ $.extend(config, settings);
3983
+ }
3984
+
3985
+ if ($(this).data('jquery.shorten') && !config.force) {
3986
+ return false;
3987
+ }
3988
+ $(this).data('jquery.shorten', true);
3989
+
3990
+ $(document).off("click", '.morelink');
3991
+
3992
+ $(document).on({
3993
+ click: function () {
3994
+
3995
+ var $this = $(this);
3996
+ if ($this.hasClass('less')) {
3997
+ $this.removeClass('less');
3998
+ $this.html(config.moreText);
3999
+ $this.parent().prev().animate({'height': '0' + '%'}, 0, function () {
4000
+ $this.parent().prev().prev().show();
4001
+ }).hide(0, function () {
4002
+ config.onLess();
4003
+ });
4004
+
4005
+ } else {
4006
+ $this.addClass('less');
4007
+ $this.html(config.lessText);
4008
+ $this.parent().prev().animate({'height': '100' + '%'}, 0, function () {
4009
+ $this.parent().prev().prev().hide();
4010
+ }).show(0, function () {
4011
+ config.onMore();
4012
+ });
4013
+ }
4014
+ return false;
4015
+ }
4016
+ }, '.morelink');
4017
+
4018
+ return this.each(function () {
4019
+ var $this = $(this);
4020
+
4021
+ var content = $this.html();
4022
+ var contentlen = $this.text().length;
4023
+ if (contentlen > config.showChars + config.minHideChars) {
4024
+ var c = content.substr(0, config.showChars);
4025
+ if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
4026
+ {
4027
+ var inTag = false; // I'm in a tag?
4028
+ var bag = ''; // Put the characters to be shown here
4029
+ var countChars = 0; // Current bag size
4030
+ var openTags = []; // Stack for opened tags, so I can close them later
4031
+ var tagName = null;
4032
+
4033
+ for (var i = 0, r = 0; r <= config.showChars; i++) {
4034
+ if (content[i] === '<' && !inTag) {
4035
+ inTag = true;
4036
+
4037
+ // This could be "tag" or "/tag"
4038
+ tagName = content.substring(i + 1, content.indexOf('>', i));
4039
+
4040
+ // If its a closing tag
4041
+ if (tagName[0] === '/') {
4042
+
4043
+ if (tagName !== ('/' + openTags[0])) {
4044
+ config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes';
4045
+ } else {
4046
+ openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!)
4047
+ }
4048
+
4049
+ } else {
4050
+ // There are some nasty tags that don't have a close tag like <br/>
4051
+ if (tagName.toLowerCase() !== 'br') {
4052
+ openTags.unshift(tagName); // Add to start the name of the tag that opens
4053
+ }
4054
+ }
4055
+ }
4056
+ if (inTag && content[i] === '>') {
4057
+ inTag = false;
4058
+ }
4059
+
4060
+ if (inTag) {
4061
+ bag += content.charAt(i);
4062
+ } // Add tag name chars to the result
4063
+ else {
4064
+ r++;
4065
+ if (countChars <= config.showChars) {
4066
+ bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
4067
+ countChars++;
4068
+ } else // Now I have the characters needed
4069
+ {
4070
+ if (openTags.length > 0) // I have unclosed tags
4071
+ {
4072
+ //console.log('They were open tags');
4073
+ //console.log(openTags);
4074
+ for (var j = 0; j < openTags.length; j++) {
4075
+ //console.log('Cierro tag ' + openTags[j]);
4076
+ bag += '</' + openTags[j] + '>'; // Close all tags that were opened
4077
+
4078
+ // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
4079
+ }
4080
+ break;
4081
+ }
4082
+ }
4083
+ }
4084
+ }
4085
+ c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html();
4086
+ } else {
4087
+ c += config.ellipsesText;
4088
+ }
4089
+
4090
+ var html = '<div class="shortcontent">' + c +
4091
+ '</div><div class="allcontent">' + content +
4092
+ '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>';
4093
+
4094
+ $this.html(html);
4095
+ $this.find(".allcontent").hide(); // Hide all text
4096
+ $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
4097
+ }
4098
+ });
4099
+ };
4100
+}
4101
+
4102
+function finalizePage() {
4103
+ // resize all charts - without starting the background thread
4104
+ // this has to be done while NETDATA is paused
4105
+ // if we ommit this, the affix menu will be wrong, since all
4106
+ // the Dom elements are initially zero-sized
4107
+ NETDATA.parseDom();
4108
+
4109
+ // ------------------------------------------------------------------------
4110
+
4111
+ NETDATA.globalPanAndZoom.callback = null;
4112
+ NETDATA.globalChartUnderlay.callback = null;
4113
+
4114
+ if (urlOptions.pan_and_zoom === true && NETDATA.options.targets.length > 0) {
4115
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
4116
+ }
4117
+
4118
+ // callback for us to track PanAndZoom operations
4119
+ NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback;
4120
+ NETDATA.globalChartUnderlay.callback = urlOptions.netdataHighlightCallback;
4121
+
4122
+ // ------------------------------------------------------------------------
4123
+
4124
+ // let it run (update the charts)
4125
+ NETDATA.unpause();
4126
+
4127
+ runOnceOnDashboardWithjQuery();
4128
+ $(".shorten").shorten();
4129
+ enableTooltipsAndPopovers();
4130
+
4131
+ if (isdemo()) {
4132
+ // do not to give errors on netdata demo servers for 60 seconds
4133
+ NETDATA.options.current.retries_on_data_failures = 60;
4134
+
4135
+ if (urlOptions.nowelcome !== true) {
4136
+ setTimeout(function () {
4137
+ $('#welcomeModal').modal();
4138
+ }, 1000);
4139
+ }
4140
+
4141
+ // google analytics when this is used for the home page of the demo sites
4142
+ // this does not run on user's installations
4143
+ setTimeout(function () {
4144
+ (function (i, s, o, g, r, a, m) {
4145
+ i['GoogleAnalyticsObject'] = r;
4146
+ i[r] = i[r] || function () {
4147
+ (i[r].q = i[r].q || []).push(arguments)
4148
+ }, i[r].l = 1 * new Date();
4149
+ a = s.createElement(o),
4150
+ m = s.getElementsByTagName(o)[0];
4151
+ a.async = 1;
4152
+ a.src = g;
4153
+ m.parentNode.insertBefore(a, m)
4154
+ })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
4155
+
4156
+ ga('create', 'UA-64295674-3', 'auto');
4157
+ ga('send', 'pageview');
4158
+ }, 2000);
4159
+ }
4160
+ else {
4161
+ notifyForUpdate();
4162
+ }
4163
+
4164
+ if (urlOptions.show_alarms === true) {
4165
+ setTimeout(function () {
4166
+ $('#alarmsModal').modal('show');
4167
+ }, 1000);
4168
+ }
4169
+
4170
+ NETDATA.onresizeCallback = function () {
4171
+ Ps.update(document.getElementById('sidebar'));
4172
+ Ps.update(document.getElementById('myNetdataDropdownUL'));
4173
+ };
4174
+ NETDATA.onresizeCallback();
4175
+
4176
+ if (netdataSnapshotData !== null) {
4177
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], netdataSnapshotData.after_ms, netdataSnapshotData.before_ms);
4178
+ }
4179
+
4180
+ // var netdataEnded = performance.now();
4181
+ // console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms');
4182
+}
4183
+
4184
+function resetDashboardOptions() {
4185
+ var help = NETDATA.options.current.show_help;
4186
+
4187
+ NETDATA.resetOptions();
4188
+ if (setTheme('slate')) {
4189
+ netdataReload();
4190
+ }
4191
+
4192
+ if (help !== NETDATA.options.current.show_help) {
4193
+ netdataReload();
4194
+ }
4195
+}
4196
+
4197
+// callback to add the dashboard info to the
4198
+// parallel javascript downloader in netdata
4199
+var netdataPrepCallback = function () {
4200
+ NETDATA.requiredCSS.push({
4201
+ url: NETDATA.serverStatic + 'css/bootstrap-toggle-2.2.2.min.css',
4202
+ isAlreadyLoaded: function () {
4203
+ return false;
4204
+ }
4205
+ });
4206
+
4207
+ NETDATA.requiredJs.push({
4208
+ url: NETDATA.serverStatic + 'lib/bootstrap-toggle-2.2.2.min.js',
4209
+ isAlreadyLoaded: function () {
4210
+ return false;
4211
+ }
4212
+ });
4213
+
4214
+ NETDATA.requiredJs.push({
4215
+ url: NETDATA.serverStatic + 'dashboard_info.js?v20181019-1',
4216
+ async: false,
4217
+ isAlreadyLoaded: function () {
4218
+ return false;
4219
+ }
4220
+ });
4221
+
4222
+ if (isdemo()) {
4223
+ document.getElementById('masthead').style.display = 'block';
4224
+ }
4225
+ else {
4226
+ if (urlOptions.update_always === true) {
4227
+ NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
4228
+ }
4229
+ }
4230
+};
4231
+
4232
+var selected_server_timezone = function (timezone, status) {
4233
+ //console.log('called with timezone: ' + timezone + ", status: " + ((typeof status === 'undefined')?'undefined':status).toString());
4234
+
4235
+ // clear the error
4236
+ document.getElementById('timezone_error_message').innerHTML = '';
4237
+
4238
+ if (typeof status === 'undefined') {
4239
+ // the user selected a timezone from the menu
4240
+
4241
+ NETDATA.setOption('user_set_server_timezone', timezone);
4242
+
4243
+ if (NETDATA.dateTime.init(timezone) === false) {
4244
+ NETDATA.dateTime.init();
4245
+
4246
+ if (!$('#local_timezone').prop('checked')) {
4247
+ $('#local_timezone').bootstrapToggle('on');
4248
+ }
4249
+
4250
+ document.getElementById('timezone_error_message').innerHTML = 'Ooops! That timezone was not accepted by your browser. Please open a github issue to help us fix it.';
4251
+ NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone);
4252
+ }
4253
+ else {
4254
+ if ($('#local_timezone').prop('checked')) {
4255
+ $('#local_timezone').bootstrapToggle('off');
4256
+ }
4257
+ }
4258
+ }
4259
+ else if (status === true) {
4260
+ // the user wants the browser default timezone to be activated
4261
+
4262
+ NETDATA.dateTime.init();
4263
+ }
4264
+ else {
4265
+ // the user wants the server default timezone to be activated
4266
+ //console.log('found ' + NETDATA.options.current.user_set_server_timezone);
4267
+
4268
+ if (NETDATA.options.current.user_set_server_timezone === 'default') {
4269
+ NETDATA.options.current.user_set_server_timezone = NETDATA.options.server_timezone;
4270
+ }
4271
+
4272
+ timezone = NETDATA.options.current.user_set_server_timezone;
4273
+
4274
+ if (NETDATA.dateTime.init(timezone) === false) {
4275
+ NETDATA.dateTime.init();
4276
+
4277
+ if (!$('#local_timezone').prop('checked')) {
4278
+ $('#local_timezone').bootstrapToggle('on');
4279
+ }
4280
+
4281
+ document.getElementById('timezone_error_message').innerHTML = 'Sorry. The timezone "' + timezone.toString() + '" is not accepted by your browser. Please select one from the list.';
4282
+ NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone);
4283
+ }
4284
+ }
4285
+
4286
+ document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone;
4287
+ return false;
4288
+};
4289
+
4290
+// our entry point
4291
+// var netdataStarted = performance.now();
4292
+
4293
+var netdataCallback = initializeDynamicDashboard;
web/gui/registry.html
deleted
-203
@@ -1,203 +0,0 @@
1
-<!DOCTYPE html>
2
-<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
3
-<html lang="en">
4
-<head>
5
- <title>NetData Registry Dashboard</title>
6
- <meta name="application-name" content="netdata">
7
-
8
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
9
- <meta charset="utf-8">
10
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
11
- <meta name="viewport" content="width=device-width, initial-scale=1">
12
- <meta name="apple-mobile-web-app-capable" content="yes">
13
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
14
-
15
- <meta property="og:locale" content="en_US" />
16
- <meta property="og:image" content="https://cloud.githubusercontent.com/assets/2662304/22945737/e98cd0c6-f2fd-11e6-96f1-5501934b0955.png"/>
17
- <meta property="og:url" content="http://my-netdata.io/"/>
18
- <meta property="og:type" content="website"/>
19
- <meta property="og:site_name" content="netdata"/>
20
- <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
21
- <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
22
-
23
- <style>
24
- .registry-container {
25
- margin-left: 50px;
26
- max-width: 400px;
27
- min-width: 200px;
28
- border: 1px solid #555;
29
- max-height: 80vh;
30
- height: auto;
31
- overflow-x: hidden;
32
- /* background-color: #3a3f44; */
33
- text-align: center;
34
- }
35
- .registry-server-container {
36
- width: 100%;
37
- height: auto;
38
- overflow-x: hidden;
39
- font-size: 10px;
40
- padding-left: 10px;
41
- padding-right: 10px;
42
- }
43
- .registry-server-container:hover {
44
- color: #f5f5f5;
45
- background-color: #262626;
46
- }
47
- .registry-server-name {
48
- display: block;
49
- text-decoration: none !important;
50
- clear: both;
51
- font-size: 16px;
52
- font-weight: bold;
53
- white-space: normal;
54
- padding-top: 10px;
55
- }
56
- </style>
57
-</head>
58
-<script>
59
-// this section has to appear before loading dashboard.js
60
-
61
-// Select a theme.
62
-// uncomment on of the two themes:
63
-
64
-// var netdataTheme = 'default'; // this is white
65
-var netdataTheme = 'slate'; // this is dark
66
-
67
-
68
-// Set the default netdata server.
69
-// on charts without a 'data-host', this one will be used.
70
-// the default is the server that dashboard.js is downloaded from.
71
-
72
-// var netdataServer = 'http://my.server:19999/';
73
-
74
-function registryGotoServer(guid) {
75
- console.log('goto server: ' + guid);
76
-}
77
-
78
-function registryAddServer(u) {
79
- return '<div id="registry_server_' + u.guid + '" class="registry-server-container" onClick="registryGotoServer(\'' + u.guid + '\'); return false;">'
80
- + '<div class="registry-server-name">' + u.name + '</div>'
81
- + '<div data-netdata="system.cpu"'
82
- + ' data-host="' + u.url + '"'
83
- + ' data-chart-library="sparkline"'
84
- + ' data-sparkline-chartrangemin="0"'
85
- + ' data-sparkline-chartrangemax="100"'
86
- + ' data-sparkline-chartrangeclip="true"'
87
- + ' data-sparkline-disabletooltips="true"'
88
- + ' data-sparkline-disableinteraction="true"'
89
- + ' data-sparkline-disablehighlight="true"'
90
- + ' data-sparkline-linecolor="#444"'
91
- + ' data-sparkline-spotcolor="disable"'
92
- + ' data-sparkline-minspotcolor="disable"'
93
- + ' data-sparkline-maxspotcolor="disable"'
94
- + ' data-width="100%"'
95
- + ' data-height="20px"'
96
- + ' data-after="-200"'
97
- + '></div>'
98
- + '</div>';
99
-}
100
-
101
-var netdataRegistryCallback = function(machines_array) {
102
- var el = '';
103
- var a1 = '';
104
- var found = 0;
105
-
106
- if(machines_array) {
107
- function name_comparator_desc(a, b) {
108
- if (a.name > b.name) return -1;
109
- if (a.name < b.name) return 1;
110
- return 0;
111
- }
112
-
113
- var machines = machines_array.sort(name_comparator_desc);
114
- var len = machines.length;
115
- while(len--) {
116
- var u = machines[len];
117
-
118
- //var status = "enabled";
119
- found++;
120
-
121
- //if(u.guid === NETDATA.registry.machine_guid)
122
- // status = "disabled";
123
-
124
- el += registryAddServer(u);
125
- a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
126
- }
127
- }
128
-
129
- if(!found) {
130
- if(machines)
131
- el += '<li><a href="https://github.com/netdata/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
132
- else
133
- el += '<li><a href="https://github.com/netdata/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
134
-
135
- a1 += '<li><a href="#"> </a></li>';
136
-
137
- el += '<li role="separator" class="divider"></li>' +
138
- '<li><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>' +
139
- '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
140
- '<li><a href="//athens.netdata.rocks/default.html">EU - Athens</a></li>';
141
- a1 += '<li role="separator" class="divider"></li>' +
142
- '<li><a href="#"> </a></li>' +
143
- '<li><a href="#"> </a></li>'+
144
- '<li><a href="#"> </a></li>';
145
- }
146
-
147
- el += '<li role="separator" class="divider"></li>';
148
- a1 += '<li role="separator" class="divider"></li>';
149
-
150
- el += '<li><a href="https://github.com/netdata/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
151
- a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>';
152
-
153
- document.getElementById('mynetdata_servers').innerHTML = el;
154
- //document.getElementById('mynetdata_servers2').innerHTML = el;
155
- //document.getElementById('mynetdata_actions1').innerHTML = a1;
156
- NETDATA.updatedDom();
157
-};
158
-</script>
159
-
160
-<!--
161
- Load dashboard.js
162
-
163
- to host this HTML file on your web server,
164
- you have to load dashboard.js from the netdata server.
165
-
166
- So, pick one the two below
167
- If you pick the first, set the server name/IP.
168
-
169
- The second assumes you host this file on /usr/share/netdata/web
170
- and that you have chown it to be owned by netdata:netdata
171
--->
172
-<!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
173
-<script type="text/javascript" src="dashboard.js?v20170724-7"></script>
174
-
175
-<script>
176
-// Set options for TV operation
177
-// This has to be done, after dashboard.js is loaded
178
-
179
-// destroy charts not shown (lowers memory on the browser)
180
-NETDATA.options.current.destroy_on_hide = true;
181
-
182
-// set this to false, to always show all dimensions
183
-NETDATA.options.current.eliminate_zero_dimensions = true;
184
-
185
-// lower the pressure on this browser
186
-NETDATA.options.current.concurrent_refreshes = true;
187
-
188
-// if the tv browser is too slow (a pi?)
189
-// set this to false
190
-NETDATA.options.current.parallel_refresher = true;
191
-
192
-// always update the charts, even if focus is lost
193
-// NETDATA.options.current.stop_updates_when_focus_is_lost = false;
194
-</script>
195
-<body>
196
-
197
-<div id="mynetdata_servers" class="registry-container" onscroll="NETDATA.onscroll();">
198
-
199
- Loading....
200
-
201
-</div> <!-- registry-container -->
202
-</body>
203
-</html>