@cryptotaxi247 / netdata-1 / commits / 3d0585546

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, '&amp;')
586 - .replace(/</g, '&lt;')
587 - .replace(/>/g, '&gt;')
588 - .replace(/"/g, '&quot;')
589 - .replace(/#/g, '&#35;')
590 - .replace(/'/g, '&#39;')
591 - .replace(/\(/g,'&#40;')
592 - .replace(/\)/g,'&#41;')
593 - .replace(/\//g,'&#47;');
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;">&nbsp;</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="#">&nbsp;</a></li>' +
1092 - '<li><a href="#">&nbsp;</a></li>'+
1093 - '<li><a href="#">&nbsp;</a></li>'+
1094 - '<li><a href="#">&nbsp;</a></li>'+
1095 - '<li><a href="#">&nbsp;</a></li>'+
1096 - '<li><a href="#">&nbsp;</a></li>'+
1097 - '<li><a href="#">&nbsp;</a></li>'+
1098 - '<li><a href="#">&nbsp;</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 - + '&nbsp;' + 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: '&nbsp;' }) + ' of real-time history.<br/>&nbsp;<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>&nbsp;red&nbsp;</b></span> is critical, <span style="color:#fe7d37"><b>&nbsp;orange&nbsp;</b></span> is warning, <span style="color: #4c1"><b>&nbsp;bright green&nbsp;</b></span> is ok, <span style="color: #9f9f9f"><b>&nbsp;light grey&nbsp;</b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b>&nbsp;black&nbsp;</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 = '&nbsp;';
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: '&nbsp;' }) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, { space: '&nbsp;' }) + '</code>'
2245 - + ((alarm.lookup_options)?(', with options <code>' + alarm.lookup_options.replace(/ /g, ',&nbsp;') + '</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/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b><br/>&nbsp;<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/>&nbsp;<br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/>&nbsp;<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&nbsp;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&nbsp;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 = '&#37;';
2278 -
2279 - html += ((typeof alarm.lookup_after !== 'undefined')?('<tr><td width="10%" style="text-align:right">db&nbsp;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&nbsp;threshold</td><td><code>' + chart.green + ' ' + units + '</code></td></tr>'):'')
2282 - + ((chart.red !== null)?('<tr><td width="10%" style="text-align:right">red&nbsp;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: '&nbsp;', negative_suffix: '' });
2289 - }
2290 - else {
2291 - delay = '<small><br/>hysteresis ';
2292 - if(alarm.delay_up_duration > 0) {
2293 - delay += 'on&nbsp;escalation&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_up_duration, { space: '&nbsp;', negative_suffix: '' }) + '</code>, ';
2294 - }
2295 - if(alarm.delay_down_duration > 0) {
2296 - delay += 'on&nbsp;recovery&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_down_duration, { space: '&nbsp;', negative_suffix: '' }) + '</code>, ';
2297 - }
2298 - }
2299 - if(alarm.delay_multiplier !== 1.0) {
2300 - delay += 'multiplied&nbsp;by&nbsp;<code>' + alarm.delay_multiplier.toString() + '</code>';
2301 - delay += ',&nbsp;up&nbsp;to&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_max_duration, { space: '&nbsp;', negative_suffix: '' }) + '</code>';
2302 - }
2303 - delay += '</small>';
2304 - }
2305 -
2306 - html += '<tr><td width="10%" style="text-align:right">check&nbsp;every</td><td>' + NETDATA.seconds4human(alarm.update_every, { space: '&nbsp;', 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, '&amp;')
23 + .replace(/</g, '&lt;')
24 + .replace(/>/g, '&gt;')
25 + .replace(/"/g, '&quot;')
26 + .replace(/#/g, '&#35;')
27 + .replace(/'/g, '&#39;')
28 + .replace(/\(/g, '&#40;')
29 + .replace(/\)/g, '&#41;')
30 + .replace(/\//g, '&#47;');
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;">&nbsp;</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="#">&nbsp;</a></li>' +
564 + '<li><a href="#">&nbsp;</a></li>' +
565 + '<li><a href="#">&nbsp;</a></li>' +
566 + '<li><a href="#">&nbsp;</a></li>' +
567 + '<li><a href="#">&nbsp;</a></li>' +
568 + '<li><a href="#">&nbsp;</a></li>' +
569 + '<li><a href="#">&nbsp;</a></li>' +
570 + '<li><a href="#">&nbsp;</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 + + '&nbsp;' + 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: '&nbsp;'}) + ' of real-time history.<br/>&nbsp;<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>&nbsp;red&nbsp;</b></span> is critical, <span style="color:#fe7d37"><b>&nbsp;orange&nbsp;</b></span> is warning, <span style="color: #4c1"><b>&nbsp;bright green&nbsp;</b></span> is ok, <span style="color: #9f9f9f"><b>&nbsp;light grey&nbsp;</b></span> is undefined (i.e. no data or no status), <span style="color: #000"><b>&nbsp;black&nbsp;</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 = '&nbsp;';
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: '&nbsp;'}) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, {space: '&nbsp;'}) + '</code>'
1812 + + ((alarm.lookup_options) ? (', with options <code>' + alarm.lookup_options.replace(/ /g, ',&nbsp;') + '</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/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b><br/>&nbsp;<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/>&nbsp;<br/><embed src="' + badge_url + '" type="image/svg+xml" height="20"/><br/>&nbsp;<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&nbsp;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&nbsp;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 = '&#37;';
1846 + }
1847 +
1848 + html += ((typeof alarm.lookup_after !== 'undefined') ? ('<tr><td width="10%" style="text-align:right">db&nbsp;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&nbsp;threshold</td><td><code>' + chart.green + ' ' + units + '</code></td></tr>') : '')
1851 + + ((chart.red !== null) ? ('<tr><td width="10%" style="text-align:right">red&nbsp;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: '&nbsp;',
1859 + negative_suffix: ''
1860 + });
1861 + }
1862 + else {
1863 + delay = '<small><br/>hysteresis ';
1864 + if (alarm.delay_up_duration > 0) {
1865 + delay += 'on&nbsp;escalation&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_up_duration, {
1866 + space: '&nbsp;',
1867 + negative_suffix: ''
1868 + }) + '</code>, ';
1869 + }
1870 + if (alarm.delay_down_duration > 0) {
1871 + delay += 'on&nbsp;recovery&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_down_duration, {
1872 + space: '&nbsp;',
1873 + negative_suffix: ''
1874 + }) + '</code>, ';
1875 + }
1876 + }
1877 + if (alarm.delay_multiplier !== 1.0) {
1878 + delay += 'multiplied&nbsp;by&nbsp;<code>' + alarm.delay_multiplier.toString() + '</code>';
1879 + delay += ',&nbsp;up&nbsp;to&nbsp;<code>' + NETDATA.seconds4human(alarm.delay_max_duration, {
1880 + space: '&nbsp;',
1881 + negative_suffix: ''
1882 + }) + '</code>';
1883 + }
1884 + delay += '</small>';
1885 + }
1886 +
1887 + html += '<tr><td width="10%" style="text-align:right">check&nbsp;every</td><td>' + NETDATA.seconds4human(alarm.update_every, {
1888 + space: '&nbsp;',
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="#">&nbsp;</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="#">&nbsp;</a></li>' +
143 - '<li><a href="#">&nbsp;</a></li>'+
144 - '<li><a href="#">&nbsp;</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 -&nbsp;
197 -<div id="mynetdata_servers" class="registry-container" onscroll="NETDATA.onscroll();">
198 -
199 - Loading....
200 -
201 -</div> <!-- registry-container -->
202 -</body>
203 -</html>