1 // You can set the following variables before loading this script:
3 // var netdataNoDygraphs = true; // do not use dygraph
4 // var netdataNoSparklines = true; // do not use sparkline
5 // var netdataNoPeitys = true; // do not use peity
6 // var netdataNoGoogleCharts = true; // do not use google
7 // var netdataNoMorris = true; // do not use morris
8 // var netdataDontStart = true; // do not start the thread to process the charts
10 // You can also set the default netdata server, using the following.
11 // When this variable is not set, we assume the page is hosted on your
12 // netdata server already.
13 // var netdataServer = "http://yourhost:19999"; // set your NetData server
17 // fix IE bug with console
18 if(!window.console){ window.console = {log: function(){} }; }
20 var NETDATA = window.NETDATA || {};
22 // ----------------------------------------------------------------------------------------------------------------
23 // Detect the netdata server
25 // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
26 // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
27 NETDATA._scriptSource = function(scripts) {
28 var script = null, base = null;
30 if(typeof document.currentScript != 'undefined') {
31 script = document.currentScript;
34 var all_scripts = document.getElementsByTagName('script');
35 script = all_scripts[all_scripts.length - 1];
38 if (script.getAttribute.length != 'undefined')
41 script = script.getAttribute('src', -1);
43 var link = document.createElement('a');
44 link.setAttribute('href', script);
46 if(!link.protocol || !link.hostname) return null;
49 if(base) base += "//";
50 base += link.hostname;
52 if(link.port) base += ":" + link.port;
58 if(typeof netdataServer != 'undefined')
59 NETDATA.serverDefault = netdataServer;
61 NETDATA.serverDefault = NETDATA._scriptSource();
63 if(NETDATA.serverDefault.slice(-1) != '/')
64 NETDATA.serverDefault += '/';
66 // default URLs for all the external files we need
67 // make them RELATIVE so that the whole thing can also be
68 // installed under a web server
69 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
70 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
71 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
72 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
73 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
74 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
75 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
76 NETDATA.dashboard_css = NETDATA.serverDefault + 'dashboard.css';
77 NETDATA.google_js = 'https://www.google.com/jsapi';
79 // these are the colors Google Charts are using
80 // we have them here to attempt emulate their look and feel on the other chart libraries
81 // http://there4.io/2012/05/02/google-chart-color-list/
82 NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
83 '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
84 '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
87 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
88 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
89 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
91 // ----------------------------------------------------------------------------------------------------------------
92 // the defaults for all charts
94 // if the user does not specify any of these, the following will be used
96 NETDATA.chartDefaults = {
97 host: NETDATA.serverDefault, // the server to get data from
98 width: '100%', // the chart width - can be null
99 height: '100%', // the chart height - can be null
100 min_width: null, // the chart minimum width - can be null
101 library: 'dygraph', // the graphing library to use
102 method: 'average', // the grouping method
103 before: 0, // panning
104 after: -600, // panning
105 pixels_per_point: 1, // the detail of the chart
106 fill_luminance: 0.8 // luminance of colors in solit areas
109 // ----------------------------------------------------------------------------------------------------------------
113 targets: null, // an array of all the DOM elements that are
114 // currently visible (independently of their
115 // viewport visibility)
117 updated_dom: true, // when true, the DOM has been updated with
118 // new elements we have to check.
120 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
121 // rendering charts continiously.
122 // used with .current.fast_render_timeframe
124 page_is_visible: true, // when true, this page is visible
126 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
127 // auto-refresher for some time (when a chart is
128 // performing pan or zoom, we need to stop refreshing
129 // all other charts, to have the maximum speed for
130 // rendering the chart that is panned or zoomed).
131 // Used with .current.global_pan_sync_time
133 last_resized: 0, // the timestamp of the last resize request
135 // the current profile
136 // we may have many...
138 pixels_per_point: 1, // the minimum pixels per point for all charts
139 // increase this to speed javascript up
140 // each chart library has its own limit too
141 // the max of this and the chart library is used
142 // the final is calculated every time, so a change
143 // here will have immediate effect on the next chart
146 idle_between_charts: 50, // ms - how much time to wait between chart updates
148 fast_render_timeframe: 200, // ms - render continously until this time of continious
149 // rendering has been reached
150 // this setting is used to make it render e.g. 10
151 // charts at once, sleep idle_between_charts time
152 // and continue for another 10 charts.
154 idle_between_loops: 200, // ms - if all charts have been updated, wait this
155 // time before starting again.
157 idle_lost_focus: 500, // ms - when the window does not have focus, check
158 // if focus has been regained, every this time
160 global_pan_sync_time: 500, // ms - when you pan or zoon a chart, the background
161 // autorefreshing of charts is paused for this amount
164 sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
165 // of time before setting up synchronized selections
168 sync_selection: true, // enable or disable selection sync
170 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
172 update_only_visible: true // enable or disable visibility management
189 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
191 window.onresize = function(event) {
192 NETDATA.options.last_resized = new Date().getTime();
195 // ----------------------------------------------------------------------------------------------------------------
198 NETDATA.errorCodes = {
199 100: { message: "Cannot load chart library", alert: true },
200 101: { message: "Cannot load jQuery", alert: true },
201 402: { message: "Chart library not found", alert: false },
202 403: { message: "Chart library not enabled/is failed", alert: false },
203 404: { message: "Chart not found", alert: false }
205 NETDATA.errorLast = {
211 NETDATA.error = function(code, msg) {
212 NETDATA.errorLast.code = code;
213 NETDATA.errorLast.message = msg;
214 NETDATA.errorLast.datetime = new Date().getTime();
216 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
218 if(NETDATA.errorCodes[code].alert)
219 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
222 NETDATA.errorReset = function() {
223 NETDATA.errorLast.code = 0;
224 NETDATA.errorLast.message = "You are doing fine!";
225 NETDATA.errorLast.datetime = 0;
228 // ----------------------------------------------------------------------------------------------------------------
231 // When multiple charts need the same chart, we avoid downloading it
232 // multiple times (and having it in browser memory multiple time)
233 // by using this registry.
235 // Every time we download a chart definition, we save it here with .add()
236 // Then we try to get it back with .get(). If that fails, we download it.
238 NETDATA.chartRegistry = {
241 add: function(host, id, data) {
242 host = host.replace(/:/g, "_").replace(/\//g, "_");
243 id = id.replace(/:/g, "_").replace(/\//g, "_");
245 if(typeof this.charts[host] == 'undefined')
246 this.charts[host] = {};
248 this.charts[host][id] = data;
251 get: function(host, id) {
252 host = host.replace(/:/g, "_").replace(/\//g, "_");
253 id = id.replace(/:/g, "_").replace(/\//g, "_");
255 if(typeof this.charts[host] == 'undefined')
258 if(typeof this.charts[host][id] == 'undefined')
261 return this.charts[host][id];
265 // ----------------------------------------------------------------------------------------------------------------
266 // Global Pan and Zoom on charts
268 // Using this structure are synchronize all the charts, so that
269 // when you pan or zoom one, all others are automatically refreshed
270 // to the same timespan.
272 NETDATA.globalPanAndZoom = {
273 seq: 0, // timestamp ms
274 // every time a chart is panned or zoomed
275 // we set the timestamp here
276 // then we use it as a sequence number
277 // to find if other charts are syncronized
280 master: null, // the master chart (state), to which all others
283 force_before_ms: null, // the timespan to sync all other charts
284 force_after_ms: null,
287 setMaster: function(state, after, before) {
288 if(!NETDATA.options.current.sync_pan_and_zoom) return;
290 if(this.master && this.master != state)
291 this.master.resetChart();
294 this.seq = new Date().getTime();
295 this.force_after_ms = after;
296 this.force_before_ms = before;
300 clearMaster: function() {
301 if(!NETDATA.options.current.sync_pan_and_zoom) return;
304 var state = this.master;
305 this.master = null; // prevent infinite recursion
308 NETDATA.options.auto_refresher_stop_until = 0;
315 this.force_after_ms = 0;
316 this.force_before_ms = 0;
319 // is the given state the master of the global
320 // pan and zoom sync?
321 isMaster: function(state) {
322 if(this.master == state) return true;
326 // are we currently have a global pan and zoom sync?
327 isActive: function() {
328 if(this.master) return true;
332 // check if a chart, other than the master
333 // needs to be refreshed, due to the global pan and zoom
334 shouldBeAutoRefreshed: function(state) {
335 if(!this.master || !this.seq)
338 if(state.needsResize())
341 if(state.follows_global == this.seq)
348 // ----------------------------------------------------------------------------------------------------------------
349 // Our state object, where all per-chart values are stored
351 NETDATA.chartInitialState = function(element) {
355 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
356 id: self.data('netdata'), // string - the name of chart
358 // the user given dimensions of the element
359 width: self.data('width') || NETDATA.chartDefaults.width,
360 height: self.data('height') || NETDATA.chartDefaults.height,
362 // string - the netdata server URL, without any path
363 host: self.data('host') || NETDATA.chartDefaults.host,
365 // string - the grouping method requested by the user
366 method: self.data('method') || NETDATA.chartDefaults.method,
368 // the time-range requested by the user
369 after: self.data('after') || NETDATA.chartDefaults.after,
370 before: self.data('before') || NETDATA.chartDefaults.before,
372 // the pixels per point requested by the user
373 pixels_per_point: self.data('pixels-per-point') || 1,
374 points: self.data('points') || null,
376 // the dimensions requested by the user
377 dimensions: self.data('dimensions') || null,
379 // the chart library requested by the user
380 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
381 library: null, // object - the chart library used
383 element: element, // the element already created by the user
384 element_chart: null, // the element with the chart
385 element_chart_id: null,
386 element_legend: null, // the element with the legend of the chart (if created by us)
387 element_legend_id: null,
389 chart_url: null, // string - the url to download chart info
390 chart: null, // object - the chart as downloaded from the server
392 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
393 created_ms: 0, // boolean - the timestamp the chart was created
394 validated: false, // boolean - has the chart been validated?
395 enabled: true, // boolean - is the chart enabled for refresh?
396 paused: false, // boolean - is the chart paused for any reason?
397 selected: false, // boolean - is the chart shown a selection?
398 debug: false, // boolena - console.log() debug info about this chart
400 updates_counter: 0, // numeric - the number of refreshes made so far
401 updates_since_last_creation: 0,
403 follows_global: 0, // the sequence number of the global synchronization
405 // Used with NETDATA.globalPanAndZoom.seq
407 last_resized: 0, // the last time the chart was resized
409 mode: null, // auto, pan, zoom
410 // this is a pointer to one of the sub-classes below
415 url: 'invalid://', // string - the last url used to update the chart
416 last_autorefreshed: 0, // milliseconds - the timestamp of last automatic refresh
417 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
418 after_ms: 0, // milliseconds - the first timestamp of the data
419 before_ms: 0, // milliseconds - the last timestamp of the data
420 points: 0, // number - the number of points in the data
421 data: null, // the last downloaded data
422 force_before_ms: null,
423 force_after_ms: null,
424 requested_before_ms: null,
425 requested_after_ms: null,
426 first_entry_ms: null,
432 url: 'invalid://', // string - the last url used to update the chart
433 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
434 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
435 after_ms: 0, // milliseconds - the first timestamp of the data
436 before_ms: 0, // milliseconds - the last timestamp of the data
437 points: 0, // number - the number of points in the data
438 data: null, // the last downloaded data
439 force_before_ms: null,
440 force_after_ms: null,
441 requested_before_ms: null,
442 requested_after_ms: null,
443 first_entry_ms: null,
449 url: 'invalid://', // string - the last url used to update the chart
450 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
451 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
452 after_ms: 0, // milliseconds - the first timestamp of the data
453 before_ms: 0, // milliseconds - the last timestamp of the data
454 points: 0, // number - the number of points in the data
455 data: null, // the last downloaded data
456 force_before_ms: null,
457 force_after_ms: null,
458 requested_before_ms: null,
459 requested_after_ms: null,
460 first_entry_ms: null,
464 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
465 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
466 refresh_dt_element: null,
469 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
472 delaySelectionSync: function() {
473 NETDATA.dygraph.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
476 setSelection: function(t) {
477 if(typeof this.library.setSelection == 'function') {
478 if(this.library.setSelection(this, t))
479 this.selected = true;
481 this.selected = false;
483 else this.selected = true;
485 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
487 return this.selected;
490 clearSelection: function() {
492 if(typeof this.library.clearSelection == 'function') {
493 if(this.library.clearSelection(this))
494 this.selected = false;
496 this.selected = true;
498 else this.selected = false;
500 if(!this.selected && this.debug) this.log('selection cleared');
503 return this.selected;
506 // find if a timestamp (ms) is shown in the current chart
507 timeIsVisible: function(t) {
508 if(t >= this.current.after_ms && t <= this.current.before_ms)
513 calculateRowForTime: function(t) {
514 if(!this.timeIsVisible(t)) return -1;
515 return Math.floor((t - this.current.after_ms) / this.current.view_update_every);
518 pauseChart: function() {
520 if(this.debug) this.log('paused');
525 unpauseChart: function() {
527 if(this.debug) this.log('unpaused');
532 resetChart: function() {
533 if(NETDATA.globalPanAndZoom.isMaster(this)) {
534 NETDATA.globalPanAndZoom.clearMaster();
535 // it will call us back - no need to do
540 //if(state.current.name != 'auto')
541 this.setMode('auto');
543 this.current.force_before_ms = null;
544 this.current.force_after_ms = null;
545 this.current.last_autorefreshed = 0;
546 this.follows_global = 0;
548 this.selected = false;
552 // do not update the chart here
553 // or the chart will flip-flop when it is the master
554 // of a selection sync and another chart becomes
556 if(!NETDATA.options.current.sync_pan_and_zoom)
560 setMode: function(m) {
562 if(this.current.name == m) return;
564 this[m].url = this.current.url;
565 this[m].last_autorefreshed = this.current.last_autorefreshed;
566 this[m].view_update_every = this.current.view_update_every;
567 this[m].after_ms = this.current.after_ms;
568 this[m].before_ms = this.current.before_ms;
569 this[m].points = this.current.points;
570 this[m].data = this.current.data;
571 this[m].requested_before_ms = this.current.requested_before_ms;
572 this[m].requested_after_ms = this.current.requested_after_ms;
573 this[m].first_entry_ms = this.current.first_entry_ms;
574 this[m].last_entry_ms = this.current.last_entry_ms;
578 this.current = this.auto;
580 this.current = this.pan;
582 this.current = this.zoom;
584 this.current = this.auto;
586 this.current.force_before_ms = null;
587 this.current.force_after_ms = null;
589 if(this.debug) this.log('mode set to ' + this.current.name);
592 _minPanOrZoomStep: function() {
593 return (((this.current.before_ms - this.current.after_ms) / this.current.points) * ((this.current.points * 5 / 100) + 1) );
594 // return this.current.view_update_every * 10;
597 _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
598 var dt_after = Math.abs(old_after - new_after);
599 var dt_before = Math.abs(old_before - new_before);
600 var old_range = old_before - old_after;
602 var new_range = new_before - new_after;
603 var dt = Math.abs(old_range - new_range);
604 var step = Math.max(dt_after, dt_before, dt);
606 var min_step = this._minPanOrZoomStep();
607 if(new_range < old_range && new_range / this.chartWidth() < 100) {
608 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum point size: 0.10, wanted point size: ' + (new_range / this.chartWidth() / 1000).toString() + ': TOO SMALL RANGE');
612 if(step >= min_step) {
613 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': YES');
617 if(this.debug) this.log('_shouldBeMoved(' + (new_after / 1000).toString() + ' - ' + (new_before / 1000).toString() + '): minimum step: ' + (min_step / 1000).toString() + ', this step: ' + (step / 1000).toString() + ': NO');
622 updateChartPanOrZoom: function(after, before, callback) {
625 if(this.current.name == 'auto') {
626 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
630 if(!this.current.force_after_ms || !this.current.force_before_ms) {
631 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
634 else if(this._shouldBeMoved(this.current.force_after_ms, this.current.force_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
635 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
638 else if(this._shouldBeMoved(this.current.requested_after_ms, this.current.requested_before_ms, after, before) && this._shouldBeMoved(this.current.after_ms, this.current.before_ms, after, before)) {
639 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
644 var now = new Date().getTime();
645 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
646 NETDATA.globalPanAndZoom.setMaster(this, after, before);
648 this.current.force_after_ms = after;
649 this.current.force_before_ms = before;
650 this.updateChart(callback);
654 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
655 if(typeof callback != 'undefined') callback();
659 createChartDOM: function() {
660 this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
661 var html = '<div class="netdata-chart netdata-'
662 + this.library_name + '-chart" id="'
663 + this.element_chart_id
664 + '"style="width: 100%; height: 100%;"></div>';
665 // + '"style="width: ' + this.chartWidth().toString() + 'px; height: ' + this.chartHeight().toString() + 'px;"></div>';
667 if(this.hasLegend()) {
668 this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
669 html += '<div class="netdata-chart-legend netdata-'
670 + this.library_name + '-legend" id="'
671 + this.element_legend_id
672 + '" style="width: ' + this.legendWidth().toString() + 'px; height: ' + this.legendHeight() + 'px;"></div>';
675 element.innerHTML = html;
676 this.element_chart = document.getElementById(this.element_chart_id);
677 $(this.element_chart).data('netdata-state-object', this);
679 if(this.hasLegend()) {
680 this.element_legend = document.getElementById(this.element_legend_id);
681 $(this.element_legend).data('netdata-state-object', this);
685 hasLegend: function() {
686 // return (this.library && this.library.legend == 'right-side');
690 legendWidth: function() {
691 return (this.hasLegend())?55:0;
694 legendHeight: function() {
695 return $(this.element).height();
698 chartWidth: function() {
699 return $(this.element).width() - this.legendWidth();
702 chartHeight: function() {
703 return $(this.element).height();
706 chartPixelsPerPoint: function() {
707 // force an options provided detail
708 var px = this.pixels_per_point;
710 if(this.library && px < this.library.pixels_per_point)
711 px = this.library.pixels_per_point;
713 if(px < NETDATA.options.current.pixels_per_point)
714 px = NETDATA.options.current.pixels_per_point;
719 needsResize: function() {
720 return (this.last_resized < NETDATA.options.last_resized);
723 resizeChart: function() {
724 if(this.needsResize()) {
725 if(this.library && !this.library.autoresize) {
726 if(this.debug) this.log('forcing re-generation due to window resize.');
728 this.last_resized = new Date().getTime();
733 chartURL: function() {
736 if(NETDATA.globalPanAndZoom.isActive()) {
737 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
738 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
739 this.follows_global = NETDATA.globalPanAndZoom.seq;
742 before = this.current.force_before_ms != null ? Math.round(this.current.force_before_ms / 1000) : this.before;
743 after = this.current.force_after_ms != null ? Math.round(this.current.force_after_ms / 1000) : this.after;
744 this.follows_global = 0;
747 this.current.requested_after_ms = after * 1000;
748 this.current.requested_before_ms = before * 1000;
750 this.current.points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
752 // build the data URL
753 this.current.url = this.chart.data_url;
754 this.current.url += "&format=" + this.library.format;
755 this.current.url += "&points=" + this.current.points.toString();
756 this.current.url += "&group=" + this.method;
757 this.current.url += "&options=" + this.library.options;
758 if(this.library.jsonWrapper) this.current.url += '|jsonwrap';
761 this.current.url += "&after=" + after.toString();
764 this.current.url += "&before=" + before.toString();
767 this.current.url += "&dimensions=" + this.dimensions;
769 if(NETDATA.options.debug.chart_data_url || this.debug) this.log('chartURL(): ' + this.current.url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.current.points + ' library: ' + this.library_name);
772 updateChartWithData: function(data) {
773 if(this.debug) this.log('got data from netdata server');
774 this.current.data = data;
776 var started = new Date().getTime();
778 // if the result is JSON, find the latest update-every
779 if(typeof data == 'object' && this.library.jsonWrapper) {
780 if(typeof data.view_update_every != 'undefined')
781 this.current.view_update_every = data.view_update_every * 1000;
783 if(typeof data.after != 'undefined')
784 this.current.after_ms = data.after * 1000;
786 if(typeof data.before != 'undefined')
787 this.current.before_ms = data.before * 1000;
789 if(typeof data.first_entry_t != 'undefined')
790 this.current.first_entry_ms = data.first_entry_t * 1000;
792 if(typeof data.last_entry_t != 'undefined')
793 this.current.last_entry_ms = data.last_entry_t * 1000;
795 if(typeof data.points != 'undefined')
796 this.current.points = data.points;
801 this.updates_counter++;
804 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
806 if(this.current.force_after_ms)
807 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
809 this.log('STATUS: forced: unset');
811 this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
812 this.log('STATUS: rendered : ' + (this.current.after_ms / 1000).toString() + ' - ' + (this.current.before_ms / 1000).toString());
813 this.log('STATUS: points : ' + (this.current.points).toString() + ', min step: ' + (this._minPanOrZoomStep() / 1000).toString());
816 // this may force the chart to be re-created
819 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate) {
820 if(this.debug) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
824 if(this.created_ms && typeof this.library.update == 'function') {
825 if(this.debug) this.log('updating chart...');
827 this.updates_since_last_creation++;
828 if(NETDATA.options.debug.chart_errors) {
829 this.library.update(this, data);
833 this.library.update(this, data);
836 this.error('chart "' + this.id + '" failed to be updated as ' + this.library_name);
841 if(this.debug) this.log('creating chart...');
843 this.createChartDOM();
844 this.updates_since_last_creation = 0;
846 if(NETDATA.options.debug.chart_errors) {
847 this.library.create(this, data);
848 this.created_ms = new Date().getTime();
852 this.library.create(this, data);
853 this.created_ms = new Date().getTime();
856 this.error('chart "' + this.id + '" failed to be created as ' + this.library_name);
861 // update the performance counters
862 var now = new Date().getTime();
864 // don't update last_update_ms if this chart is
865 // forced to be updated with global PanAndZoom
866 if(NETDATA.globalPanAndZoom.isActive())
867 this.current.last_autorefreshed = 0;
869 this.current.last_autorefreshed = now;
871 this.refresh_dt_ms = now - started;
872 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
874 if(this.refresh_dt_element)
875 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
878 updateChart: function(callback) {
879 if(!this.library || !this.library.enabled) {
880 this.error('charting library "' + this.library_name + '" is not available');
881 if(typeof callback != 'undefined') callback();
886 if(this.debug) this.error('chart "' + this.id + '" is not enabled');
887 if(typeof callback != 'undefined') callback();
891 if(!this.isVisible()) {
892 if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
893 if(typeof callback != 'undefined') callback();
897 var this_state_object = this;
898 this.getChart(function() { this_state_object.updateChart(callback); });
902 if(!this.library.initialized) {
903 var this_state_object = this;
904 this.library.initialize(function() { this_state_object.updateChart(callback); });
908 this.clearSelection();
910 if(this.debug) this.log('updating from ' + this.current.url);
912 var this_state_object = this;
914 url: this.current.url,
918 .then(function(data) {
919 this_state_object.updateChartWithData(data);
922 this_state_object.error('cannot download chart from ' + this_state_object.current.url);
925 if(typeof callback == 'function') callback();
929 isVisible: function() {
930 if(NETDATA.options.current.update_only_visible)
931 return $(this.element).visible(true);
936 isAutoRefreshed: function() {
937 return this.current.autorefresh;
940 canBeAutoRefreshed: function() {
941 if(this.isAutoRefreshed()) {
942 var now = new Date().getTime();
944 if(this.updates_counter && !NETDATA.options.page_is_visible) {
945 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
949 if(!this.isVisible()) {
950 if(this.debug) this.log('canBeAutoRefreshed(): I am not visible.');
954 // options valid only for autoRefresh()
955 if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
956 if(NETDATA.globalPanAndZoom.isActive()) {
957 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
958 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
962 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
968 if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
973 if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
977 if(now - this.current.last_autorefreshed > this.current.view_update_every) {
978 if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
987 autoRefresh: function(callback) {
988 if(this.canBeAutoRefreshed()) {
989 this.updateChart(callback);
992 if(typeof callback != 'undefined')
997 _defaultsFromDownloadedChart: function(chart) {
999 this.chart_url = chart.url;
1000 this.current.view_update_every = chart.update_every * 1000;
1001 this.current.points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1004 // fetch the chart description from the netdata server
1005 getChart: function(callback) {
1006 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
1008 this._defaultsFromDownloadedChart(this.chart);
1009 if(typeof callback == 'function') callback();
1012 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
1013 if(this.debug) this.log('downloading ' + this.chart_url);
1014 var this_state_object = this;
1017 url: this.chart_url,
1021 .done(function(chart) {
1022 chart.url = this_state_object.chart_url;
1023 chart.data_url = (this_state_object.host + chart.data_url);
1024 this_state_object._defaultsFromDownloadedChart(chart);
1025 NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
1028 NETDATA.error(404, this_state_object.chart_url);
1029 this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
1031 .always(function() {
1032 if(typeof callback == 'function') callback();
1037 // resize the chart to its real dimensions
1038 // as given by the caller
1039 sizeChart: function() {
1040 element.className += "netdata-container";
1042 if(this.debug) this.log('sizing element');
1045 $(this.element).css('width', this.width);
1048 $(this.element).css('height', this.height);
1050 // these should be left to css
1051 //$(this.element).css('display', 'inline-block')
1052 //$(this.element).css('overflow', 'hidden');
1054 if(NETDATA.chartDefaults.min_width)
1055 $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
1058 // show a message in the chart
1059 message: function(type, msg) {
1060 this.element.innerHTML = '<div class="netdata-message netdata-' + type + '-message" style="font-size: x-small; overflow: hidden; width: 100%; height: 100%;"><small>'
1064 // reset the creation datetime
1065 // since we overwrote the whole element
1067 if(this.debug) this.log(msg);
1070 // show an error on the chart and stop it forever
1071 error: function(msg) {
1072 this.message('error', this.id + ': ' + msg);
1073 this.enabled = false;
1076 // show a message indicating the chart is loading
1077 info: function(msg) {
1078 this.message('info', this.id + ': ' + msg);
1082 if(this.debug) this.log('created');
1084 this.info("loading...");
1086 // make sure the host does not end with /
1087 // all netdata API requests use absolute paths
1088 while(this.host.slice(-1) == '/')
1089 this.host = this.host.substring(0, this.host.length - 1);
1091 // check the requested library is available
1092 // we don't initialize it here - it will be initialized when
1093 // this chart will be first used
1094 if(typeof NETDATA.chartLibraries[this.library_name] == 'undefined') {
1095 NETDATA.error(402, this.library_name);
1096 this.error('chart library "' + this.library_name + '" is not found');
1098 else if(!NETDATA.chartLibraries[this.library_name].enabled) {
1099 NETDATA.error(403, this.library_name);
1100 this.error('chart library "' + this.library_name + '" is not enabled');
1103 this.library = NETDATA.chartLibraries[this.library_name];
1105 // if we need to report the rendering speed
1106 // find the element that needs to be updated
1107 if(this.refresh_dt_element_name)
1108 this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
1110 // the default mode for all charts
1111 this.setMode('auto');
1119 // get or create a chart state, given a DOM element
1120 NETDATA.chartState = function(element) {
1121 var state = $(element).data('netdata-state-object') || null;
1123 state = NETDATA.chartInitialState(element);
1124 $(element).data('netdata-state-object', state);
1129 // ----------------------------------------------------------------------------------------------------------------
1130 // Library functions
1132 // Load a script without jquery
1133 // This is used to load jquery - after it is loaded, we use jquery
1134 NETDATA._loadjQuery = function(callback) {
1135 if(typeof jQuery == 'undefined') {
1136 var script = document.createElement('script');
1137 script.type = 'text/javascript';
1138 script.async = true;
1139 script.src = NETDATA.jQuery;
1141 // script.onabort = onError;
1142 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
1143 if(typeof callback == "function")
1144 script.onload = callback;
1146 var s = document.getElementsByTagName('script')[0];
1147 s.parentNode.insertBefore(script, s);
1149 else if(typeof callback == "function")
1153 NETDATA._loadCSS = function(filename) {
1154 var fileref = document.createElement("link");
1155 fileref.setAttribute("rel", "stylesheet");
1156 fileref.setAttribute("type", "text/css");
1157 fileref.setAttribute("href", filename);
1159 if (typeof fileref != "undefined")
1160 document.getElementsByTagName("head")[0].appendChild(fileref);
1163 NETDATA.ColorLuminance = function(hex, lum) {
1164 // validate hex string
1165 hex = String(hex).replace(/[^0-9a-f]/gi, '');
1167 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
1171 // convert to decimal and change luminosity
1172 var rgb = "#", c, i;
1173 for (i = 0; i < 3; i++) {
1174 c = parseInt(hex.substr(i*2,2), 16);
1175 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
1176 rgb += ("00"+c).substr(c.length);
1182 NETDATA.guid = function() {
1184 return Math.floor((1 + Math.random()) * 0x10000)
1189 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
1192 // user function to signal us the DOM has been
1194 NETDATA.updatedDom = function() {
1195 NETDATA.options.updated_dom = true;
1198 // ----------------------------------------------------------------------------------------------------------------
1200 NETDATA.chartRefresher = function(index) {
1201 // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
1203 if(NETDATA.options.updated_dom) {
1204 // the dom has been updated
1205 // get the dom parts again
1206 NETDATA.getDomCharts(function() {
1207 NETDATA.chartRefresher(0);
1212 var target = NETDATA.options.targets.get(index);
1213 if(target == null) {
1214 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
1215 NETDATA.options.auto_refresher_fast_weight = 0;
1217 setTimeout(function() {
1218 NETDATA.chartRefresher(0);
1219 }, NETDATA.options.current.idle_between_loops);
1222 var state = NETDATA.chartState(target);
1224 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
1225 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
1227 state.autoRefresh(function() {
1228 NETDATA.chartRefresher(++index);
1232 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
1233 NETDATA.options.auto_refresher_fast_weight = 0;
1235 setTimeout(function() {
1236 state.autoRefresh(function() {
1237 NETDATA.chartRefresher(++index);
1239 }, NETDATA.options.current.idle_between_charts);
1244 NETDATA.getDomCharts = function(callback) {
1245 NETDATA.options.updated_dom = false;
1247 NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
1249 if(NETDATA.options.debug.main_loop)
1250 console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
1252 // we need to re-size all the charts quickly
1253 // before making any external calls
1254 $.each(NETDATA.options.targets, function(i, target) {
1255 // the initialization will take care of sizing
1256 // and the "loading..." message
1257 var state = NETDATA.chartState(target);
1260 if(typeof callback == 'function') callback();
1263 // this is the main function - where everything starts
1264 NETDATA.start = function() {
1265 // this should be called only once
1267 NETDATA.options.page_is_visible = true;
1269 $(window).blur(function() {
1270 NETDATA.options.page_is_visible = false;
1271 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
1274 $(window).focus(function() {
1275 NETDATA.options.page_is_visible = true;
1276 if(NETDATA.options.debug.focus) console.log('Focus restored!');
1279 if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
1280 NETDATA.options.page_is_visible = false;
1281 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1284 NETDATA.getDomCharts(function() {
1285 NETDATA.chartRefresher(0);
1289 // ----------------------------------------------------------------------------------------------------------------
1292 NETDATA.peityInitialize = function(callback) {
1293 if(typeof netdataNoPeitys == 'undefined' || !netdataNoPeitys) {
1295 url: NETDATA.peity_js,
1300 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1303 NETDATA.error(100, NETDATA.peity_js);
1305 .always(function() {
1306 if(typeof callback == "function")
1311 NETDATA.chartLibraries.peity.enabled = false;
1312 if(typeof callback == "function")
1317 NETDATA.peityChartUpdate = function(state, data) {
1318 $(state.element_chart).html(data.result);
1319 // $(state.element_chart).change() does not accept options
1320 // to pass width and height
1321 //$(state.element_chart).change();
1322 $(state.element_chart).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
1325 NETDATA.peityChartCreate = function(state, data) {
1326 $(state.element_chart).html(data.result);
1327 $(state.element_chart).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
1330 // ----------------------------------------------------------------------------------------------------------------
1333 NETDATA.sparklineInitialize = function(callback) {
1334 if(typeof netdataNoSparklines == 'undefined' || !netdataNoSparklines) {
1336 url: NETDATA.sparkline_js,
1341 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
1344 NETDATA.error(100, NETDATA.sparkline_js);
1346 .always(function() {
1347 if(typeof callback == "function")
1352 NETDATA.chartLibraries.sparkline.enabled = false;
1353 if(typeof callback == "function")
1358 NETDATA.sparklineChartUpdate = function(state, data) {
1359 state.sparkline_options.width = state.chartWidth();
1360 state.sparkline_options.height = state.chartHeight();
1362 $(state.element_chart).sparkline(data.result, state.sparkline_options);
1365 NETDATA.sparklineChartCreate = function(state, data) {
1366 var self = $(state.element);
1367 var type = self.data('sparkline-type') || 'line';
1368 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
1369 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
1370 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1371 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1372 var composite = self.data('sparkline-composite') || undefined;
1373 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
1374 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
1375 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
1376 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
1377 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
1378 var spotColor = self.data('sparkline-spotcolor') || undefined;
1379 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
1380 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
1381 var spotRadius = self.data('sparkline-spotradius') || undefined;
1382 var valueSpots = self.data('sparkline-valuespots') || undefined;
1383 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
1384 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
1385 var lineWidth = self.data('sparkline-linewidth') || undefined;
1386 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
1387 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
1388 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
1389 var xvalues = self.data('sparkline-xvalues') || undefined;
1390 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
1391 var xvalues = self.data('sparkline-xvalues') || undefined;
1392 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
1393 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
1394 var disableInteraction = self.data('sparkline-disableinteraction') || false;
1395 var disableTooltips = self.data('sparkline-disabletooltips') || false;
1396 var disableHighlight = self.data('sparkline-disablehighlight') || false;
1397 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
1398 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
1399 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
1400 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1401 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1402 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
1403 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.chart.units;
1404 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1405 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1406 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
1407 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
1408 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
1409 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
1410 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
1411 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1412 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1414 state.sparkline_options = {
1416 lineColor: lineColor,
1417 fillColor: fillColor,
1418 chartRangeMin: chartRangeMin,
1419 chartRangeMax: chartRangeMax,
1420 composite: composite,
1421 enableTagOptions: enableTagOptions,
1422 tagOptionPrefix: tagOptionPrefix,
1423 tagValuesAttribute: tagValuesAttribute,
1424 disableHiddenCheck: disableHiddenCheck,
1425 defaultPixelsPerValue: defaultPixelsPerValue,
1426 spotColor: spotColor,
1427 minSpotColor: minSpotColor,
1428 maxSpotColor: maxSpotColor,
1429 spotRadius: spotRadius,
1430 valueSpots: valueSpots,
1431 highlightSpotColor: highlightSpotColor,
1432 highlightLineColor: highlightLineColor,
1433 lineWidth: lineWidth,
1434 normalRangeMin: normalRangeMin,
1435 normalRangeMax: normalRangeMax,
1436 drawNormalOnTop: drawNormalOnTop,
1438 chartRangeClip: chartRangeClip,
1439 chartRangeMinX: chartRangeMinX,
1440 chartRangeMaxX: chartRangeMaxX,
1441 disableInteraction: disableInteraction,
1442 disableTooltips: disableTooltips,
1443 disableHighlight: disableHighlight,
1444 highlightLighten: highlightLighten,
1445 highlightColor: highlightColor,
1446 tooltipContainer: tooltipContainer,
1447 tooltipClassname: tooltipClassname,
1448 tooltipChartTitle: state.chart.title,
1449 tooltipFormat: tooltipFormat,
1450 tooltipPrefix: tooltipPrefix,
1451 tooltipSuffix: tooltipSuffix,
1452 tooltipSkipNull: tooltipSkipNull,
1453 tooltipValueLookups: tooltipValueLookups,
1454 tooltipFormatFieldlist: tooltipFormatFieldlist,
1455 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
1456 numberFormatter: numberFormatter,
1457 numberDigitGroupSep: numberDigitGroupSep,
1458 numberDecimalMark: numberDecimalMark,
1459 numberDigitGroupCount: numberDigitGroupCount,
1460 animatedZooms: animatedZooms,
1461 width: state.chartWidth(),
1462 height: state.chartHeight()
1465 $(state.element_chart).sparkline(data.result, state.sparkline_options);
1468 // ----------------------------------------------------------------------------------------------------------------
1474 dont_sync_before: 0,
1478 NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1479 if(!NETDATA.options.current.sync_selection) return;
1480 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStart()');
1482 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1483 // state.log('sync: I am not the sync master.');
1486 // state.debug = true;
1488 var t = state.current.after_ms + row * state.current.view_update_every;
1489 // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t == x)?'SAME':'DIFFERENT') + ', rows in db: ' + state.current.data.points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.current.after_ms + ' - ' + state.current.before_ms + ' real: ' + state.current.data.after + ' - ' + state.current.data.before + ' every: ' + state.current.view_update_every);
1491 now = new Date().getTime();
1492 if(now < NETDATA.dygraph.dont_sync_before) {
1493 if(state.debug) state.log('sync: cannot sync yet.');
1497 // since we are the sync master, we should not call state.setSelection()
1498 // dygraphs is taking care of visualizing our selection.
1499 state.selected = true;
1501 var dygraph = state.dygraph_instance;
1503 if(!NETDATA.dygraph.sync) {
1504 if(state.debug) state.log('sync: setting up...');
1505 $.each(NETDATA.options.targets, function(i, target) {
1506 var st = NETDATA.chartState(target);
1508 if(state.debug) st.log('sync: not adding me to sync');
1511 if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.isVisible()) {
1512 NETDATA.dygraph.slaves.push(st);
1513 if(state.debug) st.log('sync: added slave to sync');
1517 NETDATA.dygraph.sync = true;
1520 $.each(NETDATA.dygraph.slaves, function(i, st) {
1522 if(state.debug) st.log('sync: ignoring me from set selection');
1525 if(state.debug) st.log('sync: showing master selection');
1531 NETDATA.dygraph.syncStop = function(state) {
1532 if(!NETDATA.options.current.sync_selection) return;
1534 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraph.syncStop()');
1536 //if(NETDATA.dygraph.state && NETDATA.dygraph.state != state) {
1537 // state.log('sync: I am not the sync master.');
1541 if(NETDATA.dygraph.sync) {
1542 if(state.debug) st.log('sync: cleaning up...');
1543 $.each(NETDATA.dygraph.slaves, function(i, st) {
1545 if(state.debug) st.log('sync: not adding me to sync stop');
1548 if(state.debug) st.log('sync: removed slave from sync');
1549 st.clearSelection();
1553 NETDATA.dygraph.slaves = [];
1554 NETDATA.dygraph.sync = false;
1557 // since we are the sync master, we should not call state.clearSelection()
1558 // dygraphs is taking care of visualizing our selection.
1559 state.selected = false;
1562 NETDATA.dygraph.resetChart = function(state, dygraph, context) {
1563 if(NETDATA.options.debug.dygraph) state.log('dygraph.resetChart()');
1566 if(NETDATA.globalPanAndZoom.clearMaster());
1569 NETDATA.dygraph.chartPanOrZoom = function(state, dygraph, after, before) {
1570 if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(state, dygraph, after:' + after + ', before: ' + before + ')');
1572 //console.log(state.dygraph_instance);
1573 state.updateChartPanOrZoom(after, before);
1577 NETDATA.dygraphSetSelection = function(state, t) {
1578 if(typeof state.dygraph_instance != 'undefined') {
1579 var r = state.calculateRowForTime(t);
1581 state.dygraph_instance.setSelection(r);
1585 state.dygraph_instance.clearSelection();
1591 NETDATA.dygraphClearSelection = function(state, t) {
1592 if(typeof state.dygraph_instance != 'undefined') {
1593 state.dygraph_instance.clearSelection();
1598 NETDATA.dygraphInitialize = function(callback) {
1599 if(typeof netdataNoDygraphs == 'undefined' || !netdataNoDygraphs) {
1601 url: NETDATA.dygraph_js,
1606 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
1609 NETDATA.error(100, NETDATA.dygraph_js);
1611 .always(function() {
1612 if(typeof callback == "function")
1617 NETDATA.chartLibraries.dygraph.enabled = false;
1618 if(typeof callback == "function")
1623 NETDATA.dygraphChartUpdate = function(state, data) {
1624 var dygraph = state.dygraph_instance;
1626 if(state.current.name == 'pan') {
1627 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() loose update');
1628 dygraph.updateOptions({
1629 file: data.result.data,
1630 labels: data.result.labels,
1631 labelsDivWidth: state.chartWidth() - 70
1635 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() strict update');
1636 dygraph.updateOptions({
1637 file: data.result.data,
1638 labels: data.result.labels,
1639 labelsDivWidth: state.chartWidth() - 70,
1646 NETDATA.dygraphChartCreate = function(state, data) {
1647 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartCreate()');
1649 var self = $(state.element);
1650 var title = self.data('dygraph-title') || state.chart.title;
1651 var titleHeight = self.data('dygraph-titleheight') || 19;
1652 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
1653 var colors = self.data('dygraph-colors') || NETDATA.colors;
1656 var legend = self.data('dygraph-legend') || 'onmouseover';
1657 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1658 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1659 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70;
1660 var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
1661 var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
1662 var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
1663 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
1666 var sigFigs = self.data('dygraph-sigfigs') || null;
1667 var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
1668 var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
1669 var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
1672 var drawAxis = self.data('dygraph-drawaxis') || true;
1673 var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
1674 var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
1675 var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
1676 var axisLineColor = self.data('dygraph-axislinecolor') || '#DDD';
1677 var drawGrid = self.data('dygraph-drawgrid') || true;
1678 var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
1679 var drawYGrid = self.data('dygraph-drawygrid') || undefined;
1680 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1681 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1683 // enabling this makes the chart with little square lines
1684 var stepPlot = self.data('dygraph-stepplot') || false;
1685 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1687 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
1688 var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
1690 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
1691 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
1692 var drawPoints = self.data('dygraph-drawpoints') || false;
1693 var pointSize = self.data('dygraph-pointsize') || 1;
1695 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
1696 var strokeWidth = self.data('dygraph-strokewidth') || (state.chart.chart_type == 'stacked')?0.0:1.0;
1697 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1699 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
1700 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
1701 var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (state.chart.chart_type == 'stacked')?0.0:0.0;
1703 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1704 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || null; // TOO SLOW: { strokeWidth: 1.5 };
1705 var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || null; // TOO SLOW: (state.chart.chart_type == 'stacked')?0.7:0.5;
1706 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1707 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1708 var showRoller = self.data('dygraph-showroller') || false;
1710 // leave a few pixels empty on the right of the chart
1711 var rightGap = self.data('dygraph-rightgap') || 5;
1713 var fillGraph = self.data('dygraph-fillgraph') || (state.chart.chart_type == 'area')?true:false;
1714 var fillAlpha = self.data('dygraph-fillalpha') || (state.chart.chart_type == 'stacked')?0.8:0.2;
1715 var stackedGraph = self.data('dygraph-stackedgraph') || (state.chart.chart_type == 'stacked')?true:false;
1716 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1718 state.dygraph_options = {
1720 titleHeight: titleHeight,
1721 ylabel: state.chart.units,
1722 yLabelWidth: yLabelWidth,
1723 connectSeparatedPoints: connectSeparatedPoints,
1724 drawPoints: drawPoints,
1725 fillGraph: fillGraph,
1726 stackedGraph: stackedGraph,
1727 stackedGraphNaNFill: stackedGraphNaNFill,
1729 drawXGrid: drawXGrid,
1730 drawYGrid: drawYGrid,
1731 gridLinePattern: gridLinePattern,
1732 gridLineWidth: gridLineWidth,
1733 gridLineColor: gridLineColor,
1734 axisLineColor: axisLineColor,
1735 axisLineWidth: axisLineWidth,
1737 hideOverlayOnMouseOut: hideOverlayOnMouseOut,
1738 labelsDiv: labelsDiv,
1739 labelsDivStyles: labelsDivStyles,
1740 labelsDivWidth: labelsDivWidth,
1741 labelsSeparateLines: labelsSeparateLines,
1742 labelsShowZeroValues: labelsShowZeroValues,
1746 showLabelsOnHighlight: showLabelsOnHighlight,
1747 maxNumberWidth: maxNumberWidth,
1749 digitsAfterDecimal: digitsAfterDecimal,
1750 axisLabelFontSize: axisLabelFontSize,
1752 fillAlpha: fillAlpha,
1753 strokeWidth: strokeWidth,
1754 drawGapEdgePoints: drawGapEdgePoints,
1755 pointSize: pointSize,
1757 strokeBorderColor: strokeBorderColor,
1758 strokeBorderWidth: strokeBorderWidth,
1759 strokePattern: strokePattern,
1760 highlightCircleSize: highlightCircleSize,
1761 highlightSeriesOpts: highlightSeriesOpts,
1762 highlightSeriesBackgroundAlpha: highlightSeriesBackgroundAlpha,
1763 pointClickCallback: pointClickCallback,
1764 showRangeSelector: showRangeSelector,
1765 showRoller: showRoller,
1766 valueFormatter: valueFormatter,
1768 labels: data.result.labels,
1772 ticker: Dygraph.dateTicker,
1773 axisLabelFormatter: function (d, gran) {
1774 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1776 valueFormatter :function (ms) {
1777 var d = new Date(ms);
1778 return Dygraph.zeropad(d.getHours()) + ":" + Dygraph.zeropad(d.getMinutes()) + ":" + Dygraph.zeropad(d.getSeconds());
1785 drawCallback: function(dygraph, is_initial) {
1786 if(state.current.name != 'auto') {
1787 if(NETDATA.options.debug.dygraph) state.log('dygraphDrawCallback()');
1789 var x_range = dygraph.xAxisRange();
1790 var after = Math.round(x_range[0]);
1791 var before = Math.round(x_range[1]);
1793 NETDATA.dygraph.chartPanOrZoom(state, this, after, before);
1796 zoomCallback: function(minDate, maxDate, yRanges) {
1797 if(NETDATA.options.debug.dygraph) state.log('dygraphZoomCallback()');
1798 NETDATA.dygraph.syncStop(state);
1799 state.delaySelectionSync();
1800 NETDATA.dygraph.chartPanOrZoom(state, this, minDate, maxDate);
1802 highlightCallback: function(event, x, points, row, seriesName) {
1803 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphHighlightCallback()');
1805 NETDATA.dygraph.syncStart(state, event, x, points, row, seriesName);
1807 unhighlightCallback: function(event) {
1808 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphUnhighlightCallback()');
1809 state.unpauseChart();
1810 NETDATA.dygraph.syncStop(state);
1812 interactionModel : {
1813 mousedown: function(event, dygraph, context) {
1814 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousedown()');
1815 NETDATA.dygraph.syncStop(state);
1817 if(NETDATA.options.debug.dygraph) state.log('dygraphMouseDown()');
1819 // Right-click should not initiate a zoom.
1820 if(event.button && event.button == 2) return;
1822 context.initializeMouseDown(event, dygraph, context);
1824 if(event.button && event.button == 1) {
1825 if (event.altKey || event.shiftKey) {
1826 state.setMode('pan');
1827 state.delaySelectionSync();
1828 Dygraph.startPan(event, dygraph, context);
1831 state.setMode('zoom');
1832 state.delaySelectionSync();
1833 Dygraph.startZoom(event, dygraph, context);
1837 if (event.altKey || event.shiftKey) {
1838 state.setMode('zoom');
1839 state.delaySelectionSync();
1840 Dygraph.startZoom(event, dygraph, context);
1843 state.setMode('pan');
1844 state.delaySelectionSync();
1845 Dygraph.startPan(event, dygraph, context);
1849 mousemove: function(event, dygraph, context) {
1850 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousemove()');
1852 if(context.isPanning) {
1853 NETDATA.dygraph.syncStop(state);
1854 state.delaySelectionSync();
1855 state.setMode('pan');
1856 Dygraph.movePan(event, dygraph, context);
1858 else if(context.isZooming) {
1859 NETDATA.dygraph.syncStop(state);
1860 state.delaySelectionSync();
1861 state.setMode('zoom');
1862 Dygraph.moveZoom(event, dygraph, context);
1865 mouseup: function(event, dygraph, context) {
1866 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mouseup()');
1868 if (context.isPanning) {
1869 state.delaySelectionSync();
1870 Dygraph.endPan(event, dygraph, context);
1872 else if (context.isZooming) {
1873 state.delaySelectionSync();
1874 Dygraph.endZoom(event, dygraph, context);
1877 click: function(event, dygraph, context) {
1878 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.click()');
1879 Dygraph.cancelEvent(event);
1881 dblclick: function(event, dygraph, context) {
1882 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.dblclick()');
1883 NETDATA.dygraph.syncStop(state);
1884 NETDATA.dygraph.resetChart(state, dygraph, context);
1886 mousewheel: function(event, dygraph, context) {
1887 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousewheel()');
1889 NETDATA.dygraph.syncStop(state);
1890 state.delaySelectionSync();
1892 if(event.altKey || event.shiftKey) {
1893 // http://dygraphs.com/gallery/interaction-api.js
1894 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1895 var percentage = normal / 25;
1897 var before_old = state.current.before_ms;
1898 var after_old = state.current.after_ms;
1899 var range_old = before_old - after_old;
1901 var range = range_old * ( 1 - percentage );
1902 var dt = Math.round((range_old - range) / 2);
1904 var before = before_old - dt;
1905 var after = after_old + dt;
1907 if(NETDATA.options.debug.dygraph) state.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
1909 state.setMode('zoom');
1910 NETDATA.dygraph.chartPanOrZoom(state, dygraph, after, before);
1913 touchstart: function(event, dygraph, context) {
1914 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchstart()');
1915 NETDATA.dygraph.syncStop(state);
1916 state.delaySelectionSync();
1917 Dygraph.Interaction.startTouch(event, dygraph, context);
1918 context.touchDirections = { x: true, y: false };
1919 state.setMode('zoom');
1921 touchmove: function(event, dygraph, context) {
1922 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchmove()');
1923 //Dygraph.cancelEvent(event);
1924 NETDATA.dygraph.syncStop(state);
1925 Dygraph.Interaction.moveTouch(event, dygraph, context);
1927 touchend: function(event, dygraph, context) {
1928 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchend()');
1929 Dygraph.Interaction.endTouch(event, dygraph, context);
1934 state.dygraph_instance = new Dygraph(state.element_chart,
1935 data.result.data, state.dygraph_options);
1938 // ----------------------------------------------------------------------------------------------------------------
1941 NETDATA.morrisInitialize = function(callback) {
1942 if(typeof netdataNoMorris == 'undefined' || !netdataNoMorris) {
1944 // morris requires raphael
1945 if(!NETDATA.chartLibraries.raphael.initialized) {
1946 if(NETDATA.chartLibraries.raphael.enabled) {
1947 NETDATA.raphaelInitialize(function() {
1948 NETDATA.morrisInitialize(callback);
1952 NETDATA.chartLibraries.morris.enabled = false;
1953 if(typeof callback == "function")
1958 NETDATA._loadCSS(NETDATA.morris_css);
1961 url: NETDATA.morris_js,
1966 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1969 NETDATA.error(100, NETDATA.morris_js);
1971 .always(function() {
1972 if(typeof callback == "function")
1978 NETDATA.chartLibraries.morris.enabled = false;
1979 if(typeof callback == "function")
1984 NETDATA.morrisChartUpdate = function(state, data) {
1985 state.morris_instance.setData(data.result.data);
1988 NETDATA.morrisChartCreate = function(state, data) {
1990 state.morris_options = {
1991 element: state.element_chart_id,
1992 data: data.result.data,
1994 ykeys: data.dimension_names,
1995 labels: data.dimension_names,
2001 continuousLine: false,
2002 behaveLikeLine: false
2005 if(state.chart.chart_type == 'line')
2006 state.morris_instance = new Morris.Line(state.morris_options);
2008 else if(state.chart.chart_type == 'area') {
2009 state.morris_options.behaveLikeLine = true;
2010 state.morris_instance = new Morris.Area(state.morris_options);
2013 state.morris_instance = new Morris.Area(state.morris_options);
2016 // ----------------------------------------------------------------------------------------------------------------
2019 NETDATA.raphaelInitialize = function(callback) {
2020 if(typeof netdataStopRaphael == 'undefined') {
2022 url: NETDATA.raphael_js,
2027 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
2030 NETDATA.error(100, NETDATA.raphael_js);
2032 .always(function() {
2033 if(typeof callback == "function")
2038 NETDATA.chartLibraries.raphael.enabled = false;
2039 if(typeof callback == "function")
2044 NETDATA.raphaelChartUpdate = function(state, data) {
2045 $(state.element_chart).raphael(data.result, {
2046 width: state.chartWidth(),
2047 height: state.chartHeight()
2051 NETDATA.raphaelChartCreate = function(state, data) {
2052 $(state.element_chart).raphael(data.result, {
2053 width: state.chartWidth(),
2054 height: state.chartHeight()
2058 // ----------------------------------------------------------------------------------------------------------------
2061 NETDATA.googleInitialize = function(callback) {
2062 if(typeof netdataNoGoogleCharts == 'undefined' || !netdataNoGoogleCharts) {
2064 url: NETDATA.google_js,
2069 NETDATA.registerChartLibrary('google', NETDATA.google_js);
2071 google.load('visualization', '1.1', {
2072 'packages': ['corechart', 'controls'],
2073 'callback': callback
2077 NETDATA.error(100, NETDATA.google_js);
2078 if(typeof callback == "function")
2083 NETDATA.chartLibraries.google.enabled = false;
2084 if(typeof callback == "function")
2089 NETDATA.googleChartUpdate = function(state, data) {
2090 var datatable = new google.visualization.DataTable(data.result);
2091 state.google_instance.draw(datatable, state.google_options);
2094 NETDATA.googleChartCreate = function(state, data) {
2095 var datatable = new google.visualization.DataTable(data.result);
2097 state.google_options = {
2098 // do not set width, height - the chart resizes itself
2099 //width: state.chartWidth(),
2100 //height: state.chartHeight(),
2102 title: state.chart.title,
2105 // title: "Time of Day",
2106 // format:'HH:mm:ss',
2107 viewWindowMode: 'maximized',
2118 title: state.chart.units,
2119 viewWindowMode: 'pretty',
2134 focusTarget: 'category',
2141 titlePosition: 'out',
2152 curveType: 'function',
2157 switch(state.chart.chart_type) {
2159 state.google_options.vAxis.viewWindowMode = 'maximized';
2160 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2164 state.google_options.isStacked = true;
2165 state.google_options.areaOpacity = 0.85;
2166 state.google_options.vAxis.viewWindowMode = 'maximized';
2167 state.google_options.vAxis.minValue = null;
2168 state.google_options.vAxis.maxValue = null;
2169 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2174 state.google_options.lineWidth = 2;
2175 state.google_instance = new google.visualization.LineChart(state.element_chart);
2179 state.google_instance.draw(datatable, state.google_options);
2182 // ----------------------------------------------------------------------------------------------------------------
2183 // Charts Libraries Registration
2185 NETDATA.chartLibraries = {
2187 initialize: NETDATA.dygraphInitialize,
2188 create: NETDATA.dygraphChartCreate,
2189 update: NETDATA.dygraphChartUpdate,
2190 setSelection: NETDATA.dygraphSetSelection,
2191 clearSelection: NETDATA.dygraphClearSelection,
2197 legend: 'right-side',
2199 max_updates_to_recreate: 5000,
2200 pixels_per_point: 3,
2201 detects_dimensions_on_update: false
2204 initialize: NETDATA.sparklineInitialize,
2205 create: NETDATA.sparklineChartCreate,
2206 update: NETDATA.sparklineChartUpdate,
2208 clearSelection: null,
2212 options: 'flip|abs',
2214 legend: 'sparkline',
2216 max_updates_to_recreate: 5000,
2217 pixels_per_point: 3,
2218 detects_dimensions_on_update: false
2221 initialize: NETDATA.peityInitialize,
2222 create: NETDATA.peityChartCreate,
2223 update: NETDATA.peityChartUpdate,
2225 clearSelection: null,
2229 options: 'null2zero|flip|abs',
2231 legend: 'sparkline',
2233 max_updates_to_recreate: 5000,
2234 pixels_per_point: 3,
2235 detects_dimensions_on_update: false
2238 initialize: NETDATA.morrisInitialize,
2239 create: NETDATA.morrisChartCreate,
2240 update: NETDATA.morrisChartUpdate,
2242 clearSelection: null,
2246 options: 'objectrows|ms',
2250 max_updates_to_recreate: 50,
2251 pixels_per_point: 15,
2252 detects_dimensions_on_update: false
2255 initialize: NETDATA.googleInitialize,
2256 create: NETDATA.googleChartCreate,
2257 update: NETDATA.googleChartUpdate,
2259 clearSelection: null,
2262 format: 'datatable',
2267 max_updates_to_recreate: 300,
2268 pixels_per_point: 4,
2269 detects_dimensions_on_update: true
2272 initialize: NETDATA.raphaelInitialize,
2273 create: NETDATA.raphaelChartCreate,
2274 update: NETDATA.raphaelChartUpdate,
2276 clearSelection: null,
2284 max_updates_to_recreate: 1000,
2285 pixels_per_point: 1,
2286 detects_dimensions_on_update: false
2290 NETDATA.registerChartLibrary = function(library, url) {
2291 if(NETDATA.options.debug.libraries)
2292 console.log("registering chart library: " + library);
2294 NETDATA.chartLibraries[library].url = url;
2295 NETDATA.chartLibraries[library].initialized = true;
2296 NETDATA.chartLibraries[library].enabled = true;
2299 // ----------------------------------------------------------------------------------------------------------------
2302 NETDATA.errorReset();
2303 NETDATA._loadjQuery(function() {
2305 url: NETDATA.serverDefault + 'lib/visible.js',
2310 NETDATA._loadCSS(NETDATA.dashboard_css);
2312 if(typeof netdataDontStart == 'undefined' || !netdataDontStart)