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
15 //(function(window, document, undefined) {
16 // fix IE bug with console
17 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 (typeof 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 === null)
64 NETDATA.serverDefault = '';
65 else if(NETDATA.serverDefault.slice(-1) !== '/')
66 NETDATA.serverDefault += '/';
68 // default URLs for all the external files we need
69 // make them RELATIVE so that the whole thing can also be
70 // installed under a web server
71 NETDATA.jQuery = NETDATA.serverDefault + 'lib/jquery-1.11.3.min.js';
72 NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
73 NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
74 NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
75 NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined.js';
76 NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
77 NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-min.js';
78 NETDATA.morris_js = NETDATA.serverDefault + 'lib/morris.min.js';
79 NETDATA.morris_css = NETDATA.serverDefault + 'css/morris.css';
80 NETDATA.dashboard_css = NETDATA.serverDefault + 'dashboard.css';
81 NETDATA.google_js = 'https://www.google.com/jsapi';
83 // these are the colors Google Charts are using
84 // we have them here to attempt emulate their look and feel on the other chart libraries
85 // http://there4.io/2012/05/02/google-chart-color-list/
86 //NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
87 // '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
88 // '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
90 NETDATA.colors = [ '#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', '#3B3EAC',
91 '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', '#994499', '#22AA99',
92 '#6633CC', '#E67300', '#316395', '#8B0707', '#329262', '#3B3EAC' ];
94 // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
95 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
96 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
98 // ----------------------------------------------------------------------------------------------------------------
99 // the defaults for all charts
101 // if the user does not specify any of these, the following will be used
103 NETDATA.chartDefaults = {
104 host: NETDATA.serverDefault, // the server to get data from
105 width: '100%', // the chart width - can be null
106 height: '100%', // the chart height - can be null
107 min_width: null, // the chart minimum width - can be null
108 library: 'dygraph', // the graphing library to use
109 method: 'average', // the grouping method
110 before: 0, // panning
111 after: -600, // panning
112 pixels_per_point: 1, // the detail of the chart
113 fill_luminance: 0.8 // luminance of colors in solit areas
116 // ----------------------------------------------------------------------------------------------------------------
120 readyCallback: null, // a callback when we load the required stuf
121 pauseCallback: null, // a callback when we are really paused
123 pause: false, // when enabled we don't auto-refresh the charts
125 targets: null, // an array of all the state objects that are
126 // currently active (independently of their
127 // viewport visibility)
129 updated_dom: true, // when true, the DOM has been updated with
130 // new elements we have to check.
132 auto_refresher_fast_weight: 0, // this is the current time in ms, spent
133 // rendering charts continiously.
134 // used with .current.fast_render_timeframe
136 page_is_visible: true, // when true, this page is visible
138 auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the
139 // auto-refresher for some time (when a chart is
140 // performing pan or zoom, we need to stop refreshing
141 // all other charts, to have the maximum speed for
142 // rendering the chart that is panned or zoomed).
143 // Used with .current.global_pan_sync_time
145 last_resized: 0, // the timestamp of the last resize request
147 crossDomainAjax: false, // enable this to request crossDomain AJAX
149 // the current profile
150 // we may have many...
152 pixels_per_point: 1, // the minimum pixels per point for all charts
153 // increase this to speed javascript up
154 // each chart library has its own limit too
155 // the max of this and the chart library is used
156 // the final is calculated every time, so a change
157 // here will have immediate effect on the next chart
160 idle_between_charts: 100, // ms - how much time to wait between chart updates
162 fast_render_timeframe: 100, // ms - render continously until this time of continious
163 // rendering has been reached
164 // this setting is used to make it render e.g. 10
165 // charts at once, sleep idle_between_charts time
166 // and continue for another 10 charts.
168 idle_between_loops: 200, // ms - if all charts have been updated, wait this
169 // time before starting again.
171 idle_lost_focus: 500, // ms - when the window does not have focus, check
172 // if focus has been regained, every this time
174 global_pan_sync_time: 1500, // ms - when you pan or zoon a chart, the background
175 // autorefreshing of charts is paused for this amount
178 sync_selection_delay: 2500, // ms - when you pan or zoom a chart, wait this amount
179 // of time before setting up synchronized selections
182 sync_selection: true, // enable or disable selection sync
184 pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart
186 sync_pan_and_zoom: true, // enable or disable pan and zoom sync
188 update_only_visible: true, // enable or disable visibility management
190 parallel_refresher: true, // enable parallel refresh of charts
192 color_fill_opacity: {
213 if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
215 window.onresize = function(event) {
216 NETDATA.options.last_resized = new Date().getTime();
219 window.onscroll = function(event) {
220 // when the user scrolls he sees that we have
221 // hidden all the not-visible charts
222 // using this little function we try to switch
223 // the charts back to visible quickly
224 var targets = NETDATA.options.targets;
225 var len = targets.length;
226 while(len--) targets[len].isVisible();
229 // ----------------------------------------------------------------------------------------------------------------
232 NETDATA.errorCodes = {
233 100: { message: "Cannot load chart library", alert: true },
234 101: { message: "Cannot load jQuery", alert: true },
235 402: { message: "Chart library not found", alert: false },
236 403: { message: "Chart library not enabled/is failed", alert: false },
237 404: { message: "Chart not found", alert: false }
239 NETDATA.errorLast = {
245 NETDATA.error = function(code, msg) {
246 NETDATA.errorLast.code = code;
247 NETDATA.errorLast.message = msg;
248 NETDATA.errorLast.datetime = new Date().getTime();
250 console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
252 if(NETDATA.errorCodes[code].alert)
253 alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
256 NETDATA.errorReset = function() {
257 NETDATA.errorLast.code = 0;
258 NETDATA.errorLast.message = "You are doing fine!";
259 NETDATA.errorLast.datetime = 0;
262 // ----------------------------------------------------------------------------------------------------------------
265 // When multiple charts need the same chart, we avoid downloading it
266 // multiple times (and having it in browser memory multiple time)
267 // by using this registry.
269 // Every time we download a chart definition, we save it here with .add()
270 // Then we try to get it back with .get(). If that fails, we download it.
272 NETDATA.chartRegistry = {
275 fixid: function(id) {
276 return id.replace(/:/g, "_").replace(/\//g, "_");
279 add: function(host, id, data) {
280 host = this.fixid(host);
283 if(typeof this.charts[host] === 'undefined')
284 this.charts[host] = {};
286 //console.log('added ' + host + '/' + id);
287 this.charts[host][id] = data;
290 get: function(host, id) {
291 host = this.fixid(host);
294 if(typeof this.charts[host] === 'undefined')
297 if(typeof this.charts[host][id] === 'undefined')
300 //console.log('cached ' + host + '/' + id);
301 return this.charts[host][id];
304 downloadAll: function(host, callback) {
305 while(host.slice(-1) === '/')
306 host = host.substring(0, host.length - 1);
311 url: host + '/api/v1/charts',
312 crossDomain: NETDATA.options.crossDomainAjax,
316 .done(function(data) {
317 var h = NETDATA.chartRegistry.fixid(host);
318 //console.log('downloaded all charts from ' + host + ' (' + h + ')');
319 self.charts[h] = data.charts;
320 if(typeof callback === 'function')
324 if(typeof callback === 'function')
330 // ----------------------------------------------------------------------------------------------------------------
331 // Global Pan and Zoom on charts
333 // Using this structure are synchronize all the charts, so that
334 // when you pan or zoom one, all others are automatically refreshed
335 // to the same timespan.
337 NETDATA.globalPanAndZoom = {
338 seq: 0, // timestamp ms
339 // every time a chart is panned or zoomed
340 // we set the timestamp here
341 // then we use it as a sequence number
342 // to find if other charts are syncronized
345 master: null, // the master chart (state), to which all others
348 force_before_ms: null, // the timespan to sync all other charts
349 force_after_ms: null,
352 setMaster: function(state, after, before) {
353 if(!NETDATA.options.current.sync_pan_and_zoom) return;
355 if(this.master !== null && this.master !== state)
356 this.master.resetChart();
358 var now = new Date().getTime();
361 this.force_after_ms = after;
362 this.force_before_ms = before;
363 NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
367 clearMaster: function() {
368 if(!NETDATA.options.current.sync_pan_and_zoom) return;
370 if(this.master !== null) {
371 var state = this.master;
372 this.master = null; // prevent infinite recursion
375 NETDATA.options.auto_refresher_stop_until = 0;
380 this.force_after_ms = null;
381 this.force_before_ms = null;
384 // is the given state the master of the global
385 // pan and zoom sync?
386 isMaster: function(state) {
387 if(this.master === state) return true;
391 // are we currently have a global pan and zoom sync?
392 isActive: function() {
393 if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true;
397 // check if a chart, other than the master
398 // needs to be refreshed, due to the global pan and zoom
399 shouldBeAutoRefreshed: function(state) {
400 if(this.master === null || this.seq === 0)
403 if(state.needsResize())
406 if(state.follows_global === this.seq)
413 // ----------------------------------------------------------------------------------------------------------------
414 // Our state object, where all per-chart values are stored
416 chartState = function(element) {
420 uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
421 id: self.data('netdata'), // string - the name of chart
423 // the user given dimensions of the element
424 width: self.data('width') || NETDATA.chartDefaults.width,
425 height: self.data('height') || NETDATA.chartDefaults.height,
427 // string - the netdata server URL, without any path
428 host: self.data('host') || NETDATA.chartDefaults.host,
430 // string - the grouping method requested by the user
431 method: self.data('method') || NETDATA.chartDefaults.method,
433 // the time-range requested by the user
434 after: self.data('after') || NETDATA.chartDefaults.after,
435 before: self.data('before') || NETDATA.chartDefaults.before,
437 // the pixels per point requested by the user
438 pixels_per_point: self.data('pixels-per-point') || 1,
439 points: self.data('points') || null,
441 // the dimensions requested by the user
442 dimensions: self.data('dimensions') || null,
444 // the chart library requested by the user
445 library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
446 library: null, // object - the chart library used
450 element: element, // the element already created by the user
451 element_message: null,
452 element_loading: null,
453 element_chart: null, // the element with the chart
454 element_chart_id: null,
455 element_legend: null, // the element with the legend of the chart (if created by us)
456 element_legend_id: null,
457 element_legend_childs: {
465 chart_url: null, // string - the url to download chart info
466 chart: null, // object - the chart as downloaded from the server
468 downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
469 created_ms: 0, // boolean - the timestamp the chart was created
470 validated: false, // boolean - has the chart been validated?
471 enabled: true, // boolean - is the chart enabled for refresh?
472 paused: false, // boolean - is the chart paused for any reason?
473 selected: false, // boolean - is the chart shown a selection?
474 debug: false, // boolean - console.log() debug info about this chart
476 updates_counter: 0, // numeric - the number of refreshes made so far
477 updates_since_last_creation: 0,
479 follows_global: 0, // the sequence number of the global synchronization
481 // Used with NETDATA.globalPanAndZoom.seq
483 last_resized: 0, // the last time the chart was resized
485 mode: null, // auto, pan, zoom
486 // this is a pointer to one of the sub-classes below
491 url: 'invalid://', // string - the last url used to update the chart
492 last_autorefreshed: 0, // milliseconds - the timestamp of last automatic refresh
493 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
494 after_ms: 0, // milliseconds - the first timestamp of the data
495 before_ms: 0, // milliseconds - the last timestamp of the data
496 points: 0, // number - the number of points in the data
497 data: null, // the last downloaded data
498 force_update_at: 0, // the timestamp to force the update at
499 force_before_ms: null,
500 force_after_ms: null,
501 requested_before_ms: null,
502 requested_after_ms: null,
503 first_entry_ms: null,
509 url: 'invalid://', // string - the last url used to update the chart
510 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
511 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
512 after_ms: 0, // milliseconds - the first timestamp of the data
513 before_ms: 0, // milliseconds - the last timestamp of the data
514 points: 0, // number - the number of points in the data
515 data: null, // the last downloaded data
516 force_update_at: 0, // the timestamp to force the update at
517 force_before_ms: null,
518 force_after_ms: null,
519 requested_before_ms: null,
520 requested_after_ms: null,
521 first_entry_ms: null,
527 url: 'invalid://', // string - the last url used to update the chart
528 last_autorefreshed: 0, // milliseconds - the timestamp of last refresh
529 view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
530 after_ms: 0, // milliseconds - the first timestamp of the data
531 before_ms: 0, // milliseconds - the last timestamp of the data
532 points: 0, // number - the number of points in the data
533 data: null, // the last downloaded data
534 force_update_at: 0, // the timestamp to force the update at
535 force_before_ms: null,
536 force_after_ms: null,
537 requested_before_ms: null,
538 requested_after_ms: null,
539 first_entry_ms: null,
543 refresh_dt_ms: 0, // milliseconds - the time the last refresh took
544 refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
545 refresh_dt_element: null
551 // ----------------------------------------------------------------------------------------------------------------
552 // global selection sync
554 NETDATA.globalSelectionSync = {
560 // prevent to global selection sync for some time
561 chartState.prototype.globalSelectionSyncDelay = function(ms) {
562 if(!NETDATA.options.current.sync_selection) return;
563 if(typeof ms === 'number')
564 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
566 NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
569 // can we globally apply selection sync?
570 chartState.prototype.globalSelectionSyncAbility = function() {
571 if(!NETDATA.options.current.sync_selection) return false;
572 if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) return false;
576 chartState.prototype.globalSelectionSyncIsMaster = function() {
577 if(NETDATA.globalSelectionSync.state === this)
583 // this chart is the master of the global selection sync
584 chartState.prototype.globalSelectionSyncBeMaster = function() {
586 if(this.globalSelectionSyncIsMaster()) {
587 if(this.debug) this.log('sync: I am the master already.');
591 if(NETDATA.globalSelectionSync.state) {
592 if(this.debug) this.log('sync: I am not the sync master. Resetting global sync.');
593 this.globalSelectionSyncStop();
597 if(this.debug) this.log('sync: becoming sync master.');
598 this.selected = true;
599 NETDATA.globalSelectionSync.state = this;
601 // find the all slaves
602 var targets = NETDATA.options.targets;
603 var len = targets.length;
608 if(this.debug) st.log('sync: not adding me to sync');
610 else if(st.globalSelectionSyncIsEligible()) {
611 if(this.debug) st.log('sync: adding to sync as slave');
612 st.globalSelectionSyncBeSlave();
616 // this.globalSelectionSyncDelay(100);
619 // can the chart participate to the global selection sync as a slave?
620 chartState.prototype.globalSelectionSyncIsEligible = function() {
621 if(this.library !== null && typeof this.library.setSelection === 'function' && this.isVisible() && this.created_ms !== 0)
626 // this chart is a slave of the global selection sync
627 chartState.prototype.globalSelectionSyncBeSlave = function() {
628 if(NETDATA.globalSelectionSync.state !== this)
629 NETDATA.globalSelectionSync.slaves.push(this);
632 // sync all the visible charts to the given time
633 // this is to be called from the chart libraries
634 chartState.prototype.globalSelectionSync = function(t) {
635 if(!this.globalSelectionSyncAbility()) {
636 if(this.debug) this.log('sync: cannot sync (yet?).');
640 if(!this.globalSelectionSyncIsMaster()) {
641 if(this.debug) this.log('sync: trying to be sync master.');
642 this.globalSelectionSyncBeMaster();
644 if(!this.globalSelectionSyncAbility()) {
645 if(this.debug) this.log('sync: cannot sync (yet?).');
651 // var start = new Date().getTime();
653 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
658 // var end = new Date().getTime();
659 // console.log(end - start);
662 // stop syncing all charts to the given time
663 chartState.prototype.globalSelectionSyncStop = function() {
664 if(NETDATA.globalSelectionSync.slaves.length) {
665 if(this.debug) this.log('sync: cleaning up...');
667 $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
669 if(self.debug) st.log('sync: not adding me to sync stop');
672 if(self.debug) st.log('sync: removed slave from sync');
677 NETDATA.globalSelectionSync.slaves = [];
678 NETDATA.globalSelectionSync.state = null;
681 // since we are the sync master, we should not call this.clearSelection()
682 // dygraphs is taking care of visualizing our selection.
683 this.selected = false;
686 chartState.prototype.setSelection = function(t) {
687 if(typeof this.library.setSelection === 'function') {
688 if(this.library.setSelection(this, t))
689 this.selected = true;
691 this.selected = false;
693 else this.selected = true;
695 if(this.selected && this.debug) this.log('selection set to ' + t.toString());
697 return this.selected;
700 chartState.prototype.clearSelection = function() {
702 if(typeof this.library.clearSelection === 'function') {
703 if(this.library.clearSelection(this))
704 this.selected = false;
706 this.selected = true;
708 else this.selected = false;
710 if(!this.selected && this.debug) this.log('selection cleared');
714 return this.selected;
717 // find if a timestamp (ms) is shown in the current chart
718 chartState.prototype.timeIsVisible = function(t) {
719 if(t >= this.current.after_ms && t <= this.current.before_ms)
724 chartState.prototype.calculateRowForTime = function(t) {
725 if(!this.timeIsVisible(t)) return -1;
726 return Math.floor((t - this.current.after_ms) / this.current.view_update_every);
729 // ----------------------------------------------------------------------------------------------------------------
732 chartState.prototype.log = function(msg) {
733 console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
736 chartState.prototype.pauseChart = function() {
738 if(this.debug) this.log('paused');
743 chartState.prototype.unpauseChart = function() {
745 if(this.debug) this.log('unpaused');
750 chartState.prototype.resetChart = function() {
751 if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible())
752 NETDATA.globalPanAndZoom.clearMaster();
754 this.follows_global = 0;
756 this.clearSelection();
758 this.setMode('auto');
759 this.current.force_update_at = 0;
760 this.current.force_before_ms = null;
761 this.current.force_after_ms = null;
762 this.current.last_autorefreshed = 0;
764 this.selected = false;
768 // do not update the chart here
769 // or the chart will flip-flop when it is the master
770 // of a selection sync and another chart becomes
772 if(!NETDATA.options.current.sync_pan_and_zoom && this.isVisible())
776 chartState.prototype.setMode = function(m) {
778 if(this.current.name === m) return;
780 this[m].url = this.current.url;
781 this[m].last_autorefreshed = this.current.last_autorefreshed;
782 this[m].view_update_every = this.current.view_update_every;
783 this[m].after_ms = this.current.after_ms;
784 this[m].before_ms = this.current.before_ms;
785 this[m].points = this.current.points;
786 this[m].data = this.current.data;
787 this[m].requested_before_ms = this.current.requested_before_ms;
788 this[m].requested_after_ms = this.current.requested_after_ms;
789 this[m].first_entry_ms = this.current.first_entry_ms;
790 this[m].last_entry_ms = this.current.last_entry_ms;
794 this.current = this.auto;
796 this.current = this.pan;
797 else if(m === 'zoom')
798 this.current = this.zoom;
800 this.current = this.auto;
802 this.current.force_update_at = 0;
803 this.current.force_before_ms = null;
804 this.current.force_after_ms = null;
806 if(this.debug) this.log('mode set to ' + this.current.name);
809 chartState.prototype._minPanOrZoomStep = function() {
810 return (((this.current.before_ms - this.current.after_ms) / this.current.points) * ((this.current.points * 5 / 100) + 1) );
811 // return this.current.view_update_every * 10;
814 chartState.prototype._shouldBeMoved = function(old_after, old_before, new_after, new_before) {
815 var dt_after = Math.abs(old_after - new_after);
816 var dt_before = Math.abs(old_before - new_before);
817 var old_range = old_before - old_after;
819 var new_range = new_before - new_after;
820 var dt = Math.abs(old_range - new_range);
821 var step = Math.max(dt_after, dt_before, dt);
823 var min_step = this._minPanOrZoomStep();
824 if(new_range < old_range && new_range / this.chartWidth() < 100) {
825 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');
829 if(step >= min_step) {
830 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');
834 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');
839 chartState.prototype.updateChartPanOrZoom = function(after, before) {
842 if(this.current.name === 'auto') {
843 if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
847 if(!this.current.force_after_ms || !this.current.force_before_ms) {
848 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
851 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)) {
852 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());
855 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)) {
856 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());
861 this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
862 this.current.force_after_ms = after;
863 this.current.force_before_ms = before;
864 NETDATA.globalPanAndZoom.setMaster(this, after, before);
868 if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
872 chartState.prototype.legendFormatValue = function(value) {
873 if(value === null) return '';
874 if(typeof value !== 'number') return value;
876 var abs = Math.abs(value);
877 if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
878 if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString();
879 return (Math.round(value * 10000) / 10000).toLocaleString();
882 chartState.prototype.legendSetLabelValue = function(label, value) {
883 var series = this.element_legend_childs.series[label];
885 if(typeof series === 'undefined') return;
887 value = this.legendFormatValue(value);
889 // if the value has not changed, skip DOM update
890 if(series.last === value) return;
893 if(series.value !== null) series.value.innerHTML = value;
894 if(series.user !== null) series.user.innerHTML = value;
897 chartState.prototype.legendSetDate = function(ms) {
898 if(typeof ms !== 'number') {
899 this.legendUndefined();
903 var d = new Date(ms);
905 if(this.element_legend_childs.title_date)
906 this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
908 if(this.element_legend_childs.title_time)
909 this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
911 if(this.element_legend_childs.title_units)
912 this.element_legend_childs.title_units.innerHTML = this.chart.units;
915 chartState.prototype.legendUndefined = function() {
916 if(this.element_legend_childs.title_date)
917 this.element_legend_childs.title_date.innerHTML = ' ';
919 if(this.element_legend_childs.title_time)
920 this.element_legend_childs.title_time.innerHTML = this.chart.name;
922 if(this.element_legend_childs.title_units)
923 this.element_legend_childs.title_units.innerHTML = ' ';
926 chartState.prototype.legendShowLatestValues = function() {
927 if(this.chart === null) return;
928 if(this.selected) return;
930 if(this.current.data === null || this.element_legend_childs.series === null) {
931 this.legendUndefined();
935 if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every)
936 this.legendSetDate(this.current.data.before * 1000);
938 this.legendUndefined();
940 var labels = this.current.data.dimension_names;
941 var i = labels.length;
943 var label = labels[i];
945 if(typeof label === 'undefined') continue;
946 if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
948 if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every)
949 this.legendSetLabelValue(label, this.current.data.result_latest_values[i]);
951 this.legendSetLabelValue(label, null);
955 chartState.prototype.legendReset = function() {
956 this.legendShowLatestValues();
959 chartState.prototype.chartColors = function() {
960 if(this.colors !== null) return this.colors;
962 this.colors = $(this.element).data('colors');
963 if(typeof this.colors === 'undefined' || this.colors === null)
964 this.colors = NETDATA.colors;
966 // console.log(this.colors);
968 if(typeof s === 'string') s = s.split(' ');
970 this.colors = new Array();
972 $.each(s, function(i, c) {
976 // push the default colors too
978 $.each(NETDATA.colors, function(i, c) {
986 chartState.prototype.legendUpdateDOM = function() {
987 if(!this.hasLegend()) return;
991 // check that the legend DOM is up to date for the downloaded dimensions
992 if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
993 // this.log('the legend does not have any series - requesting legend update');
996 else if(this.current.data === null) {
997 // this.log('the chart does not have any data - requesting legend update');
1001 // this.log('checking existing legend');
1002 var labels = this.current.data.dimension_names;
1003 var i = labels.length;
1005 var name = labels[i];
1006 if(typeof this.element_legend_childs.series[name] === 'undefined') {
1007 // this.log('legend is incosistent - missing dimension:' + name);
1011 else if(Math.abs(this.current.data.last_entry_t - this.current.data.before) <= this.current.data.view_update_every) {
1012 // this.log('setting legend of ' + name + ' to ' + this.current.data.latest_values[i]);
1013 this.legendSetLabelValue(name, this.current.data.latest_values[i]);
1020 if(this.debug) this.log('updating Legend DOM');
1022 this.element_legend.innerHTML = '';
1024 this.element_legend_childs = {
1025 title_date: document.createElement('span'),
1026 title_time: document.createElement('span'),
1027 title_units: document.createElement('span'),
1031 this.element_legend_childs.title_date.className += " netdata-legend-title-date";
1032 this.element_legend.appendChild(this.element_legend_childs.title_date);
1034 this.element_legend.appendChild(document.createElement('br'));
1036 this.element_legend_childs.title_time.className += " netdata-legend-title-time";
1037 this.element_legend.appendChild(this.element_legend_childs.title_time);
1039 this.element_legend.appendChild(document.createElement('br'));
1041 this.element_legend_childs.title_units.className += " netdata-legend-title-units";
1042 this.element_legend.appendChild(this.element_legend_childs.title_units);
1044 this.element_legend.appendChild(document.createElement('br'));
1046 var nano = document.createElement('div');
1047 nano.className = 'netdata-legend-series';
1048 this.element_legend.appendChild(nano);
1050 var content = document.createElement('div');
1051 content.className = 'netdata-legend-series-content';
1052 nano.appendChild(content);
1055 var genLabel = function(state, parent, name, count) {
1056 var c = count % state.chartColors().length;
1058 var user_element = null;
1059 var user_id = self.data('show-value-of-' + name + '-at') || null;
1060 if(user_id) user_element = document.getElementById(user_id);
1062 state.element_legend_childs.series[name] = {
1063 name: document.createElement('span'),
1064 value: document.createElement('span'),
1069 var label = state.element_legend_childs.series[name];
1071 label.name.className += ' netdata-legend-name';
1072 label.value.className += ' netdata-legend-value';
1073 label.name.title = name;
1074 label.value.title = name;
1076 var rgb = NETDATA.colorHex2Rgb(state.chartColors()[c]);
1077 label.name.innerHTML = '<table class="netdata-legend-name-table-'
1078 + state.chart.chart_type
1079 + '" style="background-color: '
1080 + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current.color_fill_opacity[state.chart.chart_type] + ')'
1081 + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
1083 var text = document.createTextNode(' ' + name);
1084 label.name.appendChild(text);
1086 label.name.style.color = state.chartColors()[c];
1087 label.value.style.color = state.chartColors()[c];
1090 parent.appendChild(document.createElement('br'));
1092 parent.appendChild(label.name);
1093 parent.appendChild(label.value);
1096 if(this.current.data) {
1098 $.each(me.current.data.dimension_names, function(i, d) {
1099 genLabel(me, content, d, i);
1104 $.each(me.chart.dimensions, function(i, d) {
1105 genLabel(me, content, d.name, i);
1109 // create a hidden div to be used for hidding
1110 // the original legend of the chart library
1111 var el = document.createElement('div');
1112 this.element_legend.appendChild(el);
1113 el.style.display = 'none';
1115 this.element_legend_childs.hidden = document.createElement('div');
1116 el.appendChild(this.element_legend_childs.hidden);
1117 nano.appendChild(el);
1119 $(nano).nanoScroller({
1120 paneClass: 'netdata-legend-series-pane',
1121 sliderClass: 'netdata-legend-series-slider',
1122 contentClass: 'netdata-legend-series-content',
1123 enabledClass: '__enabled',
1124 flashedClass: '__flashed',
1125 activeClass: '__active',
1130 this.legendShowLatestValues();
1133 chartState.prototype.createChartDOM = function() {
1134 if(this.debug) this.log('creating DOM');
1136 if(this.hasLegend()) {
1137 this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
1138 this.element_chart = document.createElement('div');
1139 this.element_chart.className += ' netdata-chart-with-legend-right';
1140 this.element_chart.className += ' netdata-' + this.library_name + '-chart-with-legend-right';
1141 this.element_chart.id = this.element_chart_id;
1142 $(this.element_chart).data('netdata-state-object', this);
1143 this.element.appendChild(this.element_chart);
1145 this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
1146 this.element_legend = document.createElement('div');
1147 this.element_legend.className += ' netdata-chart-legend';
1148 this.element_legend.className += ' netdata-' + this.library_name + '-legend';
1149 this.element_legend.id = this.element_legend_id;
1150 $(this.element_legend).data('netdata-state-object', this);
1151 this.element.appendChild(this.element_legend);
1152 this.element_legend_childs.series = null;
1153 this.legendUpdateDOM();
1156 this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
1157 this.element_chart = document.createElement('div');
1158 this.element_chart.className += ' netdata-chart';
1159 this.element_chart.className += ' netdata-' + this.library_name + '-chart';
1160 this.element_chart.id = this.element_chart_id;
1161 $(this.element_chart).data('netdata-state-object', this);
1162 this.element.appendChild(this.element_chart);
1164 this.element_legend_id = null;
1165 this.element_legend = null;
1166 this.element_legend_childs.series = null;
1169 // in case the user has an active global selection sync in place
1171 this.globalSelectionSyncStop();
1174 chartState.prototype.hasLegend = function() {
1175 if(typeof this.___hasLegendCache___ !== 'undefined')
1176 return this.___hasLegendCache___;
1179 if(this.library && this.library.legend(this) === 'right-side') {
1180 var legend = $(this.element).data('legend') || 'yes';
1181 if(legend === 'yes') leg = true;
1184 this.___hasLegendCache___ = leg;
1188 chartState.prototype.legendWidth = function() {
1189 return (this.hasLegend())?110:0;
1192 chartState.prototype.legendHeight = function() {
1193 return $(this.element).height();
1196 chartState.prototype.chartWidth = function() {
1197 return $(this.element).width() - this.legendWidth();
1200 chartState.prototype.chartHeight = function() {
1201 return $(this.element).height();
1204 chartState.prototype.chartPixelsPerPoint = function() {
1205 // force an options provided detail
1206 var px = this.pixels_per_point;
1208 if(this.library && px < this.library.pixels_per_point(this))
1209 px = this.library.pixels_per_point(this);
1211 if(px < NETDATA.options.current.pixels_per_point)
1212 px = NETDATA.options.current.pixels_per_point;
1217 chartState.prototype.needsResize = function() {
1218 return (this.library && !this.library.autoresize() && this.last_resized < NETDATA.options.last_resized);
1221 chartState.prototype.resizeChart = function() {
1222 if(this.needsResize()) {
1223 if(this.debug) this.log('forcing re-generation due to window resize.');
1224 this.created_ms = 0;
1225 this.last_resized = new Date().getTime();
1229 chartState.prototype.chartURL = function() {
1232 if(NETDATA.globalPanAndZoom.isActive()) {
1233 after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
1234 before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
1235 this.follows_global = NETDATA.globalPanAndZoom.seq;
1238 before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before;
1239 after = this.current.force_after_ms !== null ? Math.round(this.current.force_after_ms / 1000) : this.after;
1240 this.follows_global = 0;
1243 this.current.requested_after_ms = after * 1000;
1244 this.current.requested_before_ms = before * 1000;
1246 this.current.points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1248 // build the data URL
1249 this.current.url = this.chart.data_url;
1250 this.current.url += "&format=" + this.library.format();
1251 this.current.url += "&points=" + this.current.points.toString();
1252 this.current.url += "&group=" + this.method;
1253 this.current.url += "&options=" + this.library.options();
1254 this.current.url += '|jsonwrap';
1257 this.current.url += "&after=" + after.toString();
1260 this.current.url += "&before=" + before.toString();
1263 this.current.url += "&dimensions=" + this.dimensions;
1265 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);
1268 chartState.prototype.updateChartWithData = function(data) {
1269 if(this.debug) this.log('got data from netdata server');
1270 this.current.data = data;
1272 var started = new Date().getTime();
1274 // if the result is JSON, find the latest update-every
1275 if(typeof data === 'object') {
1276 if(typeof data.view_update_every !== 'undefined')
1277 this.current.view_update_every = data.view_update_every * 1000;
1279 if(typeof data.after !== 'undefined')
1280 this.current.after_ms = data.after * 1000;
1282 if(typeof data.before !== 'undefined')
1283 this.current.before_ms = data.before * 1000;
1285 if(typeof data.first_entry_t !== 'undefined')
1286 this.current.first_entry_ms = data.first_entry_t * 1000;
1288 if(typeof data.last_entry_t !== 'undefined')
1289 this.current.last_entry_ms = data.last_entry_t * 1000;
1291 if(typeof data.points !== 'undefined')
1292 this.current.points = data.points;
1297 this.updates_counter++;
1300 this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
1302 if(this.current.force_after_ms)
1303 this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
1305 this.log('STATUS: forced: unset');
1307 this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
1308 this.log('STATUS: rendered : ' + (this.current.after_ms / 1000).toString() + ' - ' + (this.current.before_ms / 1000).toString());
1309 this.log('STATUS: points : ' + (this.current.points).toString() + ', min step: ' + (this._minPanOrZoomStep() / 1000).toString());
1312 // this may force the chart to be re-created
1315 if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
1316 if(this.debug) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
1317 this.created_ms = 0;
1320 if(this.created_ms > 0 && typeof this.library.update === 'function') {
1321 if(this.debug) this.log('updating chart...');
1323 // check and update the legend
1324 this.legendUpdateDOM();
1326 this.updates_since_last_creation++;
1327 if(NETDATA.options.debug.chart_errors) {
1328 this.library.update(this, data);
1332 this.library.update(this, data);
1335 this.error('chart "' + this.id + '" failed to be updated as ' + this.library_name);
1340 if(this.debug) this.log('creating chart...');
1342 this.createChartDOM();
1343 this.updates_since_last_creation = 0;
1345 if(NETDATA.options.debug.chart_errors) {
1346 this.library.create(this, data);
1347 this.created_ms = new Date().getTime();
1351 this.library.create(this, data);
1352 this.created_ms = new Date().getTime();
1355 this.error('chart "' + this.id + '" failed to be created as ' + this.library_name);
1359 this.legendShowLatestValues();
1361 // update the performance counters
1362 var now = new Date().getTime();
1364 // don't update last_autorefreshed if this chart is
1365 // forced to be updated with global PanAndZoom
1366 if(NETDATA.globalPanAndZoom.isActive())
1367 this.current.last_autorefreshed = 0;
1369 this.current.last_autorefreshed = now;
1371 this.refresh_dt_ms = now - started;
1372 NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
1374 if(this.refresh_dt_element)
1375 this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
1378 chartState.prototype.updateChart = function(callback) {
1379 // due to late initialization of charts and libraries
1380 // we need to check this too
1381 if(this.enabled === false) {
1382 if(this.debug) this.log('I am not enabled');
1383 if(typeof callback === 'function') callback();
1387 if(this.chart === null) {
1389 this.getChart(function() { self.updateChart(callback); });
1393 if(this.library.initialized === false) {
1395 this.library.initialize(function() { self.updateChart(callback); });
1399 this.clearSelection();
1402 if(this.debug) this.log('updating from ' + this.current.url);
1405 this.xhr = $.ajax( {
1406 url: this.current.url,
1407 crossDomain: NETDATA.options.crossDomainAjax,
1411 .success(function(data) {
1413 if(self.debug) self.log('data received. updating chart.');
1414 self.updateChartWithData(data);
1418 self.error('data download failed for url: ' + self.current.url);
1420 .always(function() {
1422 if(typeof callback === 'function') callback();
1426 chartState.prototype.unhideChart = function() {
1427 if(typeof this.___isHidden___ !== 'undefined') {
1428 this.element_message.style.display = 'none';
1429 if(this.element_chart !== null) this.element_chart.style.display = 'inline-block';
1430 if(this.element_legend !== null) this.element_legend.style.display = 'inline-block';
1431 if(this.element_loading !== null) this.element_loading.style.display = 'none';
1432 this.___isHidden___ = undefined;
1433 this.element_message.innerHTML = 'chart ' + this.id + ' is visible now';
1437 chartState.prototype.hideChart = function() {
1438 if(typeof this.___isHidden___ === 'undefined') {
1439 this.element_message.style.display = 'inline-block';
1440 if(this.element_chart !== null) this.element_chart.style.display = 'none';
1441 if(this.element_legend !== null) this.element_legend.style.display = 'none';
1442 if(this.element_loading !== null) this.element_loading.style.display = 'none';
1443 this.___isHidden___ = true;
1444 this.element_message.innerHTML = 'chart ' + this.id + ' is hidden to speed up the browser';
1448 chartState.prototype.hideLoading = function() {
1449 if(typeof this.___showsLoading___ !== 'undefined') {
1450 this.element_message.style.display = 'none';
1451 if(this.element_chart !== null) this.element_chart.style.display = 'inline-block';
1452 if(this.element_legend !== null) this.element_legend.style.display = 'inline-block';
1453 if(this.element_loading !== null) this.element_loading.style.display = 'none';
1454 this.___showsLoading___ = undefined;
1455 this.element_loading.innerHTML = 'chart ' + this.id + ' finished loading!';
1459 chartState.prototype.showLoading = function() {
1460 if(typeof this.___showsLoading___ === 'undefined' && this.created_ms === 0) {
1461 this.element_message.style.display = 'none';
1462 if(this.element_chart !== null) this.element_chart.style.display = 'none';
1463 if(this.element_legend !== null) this.element_legend.style.display = 'none';
1464 if(this.element_loading !== null) this.element_loading.style.display = 'inline-block';
1465 this.___showsLoading___ = true;
1466 this.element_loading.innerHTML = 'chart ' + this.id + ' is loading...';
1470 chartState.prototype.isVisible = function() {
1471 var wh = window.innerHeight;
1472 var x = this.element.getBoundingClientRect();
1477 if(x.top < 0 && -x.top > x.height) {
1478 // the chart is entirely above
1479 ret = -x.top - x.height;
1481 else if(x.top > wh) {
1482 // the chart is entirely below
1486 if(ret > tolerance) {
1487 // the chart is too far
1488 if(this.created_ms !== 0) this.hideChart();
1492 // the chart is inside or very close
1497 chartState.prototype.isAutoRefreshed = function() {
1498 return (this.current.autorefresh);
1501 chartState.prototype.canBeAutoRefreshed = function() {
1502 now = new Date().getTime();
1504 if(this.enabled === false) {
1505 if(this.debug) this.log('I am not enabled');
1509 if(this.library === null || this.library.enabled === false) {
1510 this.error('charting library "' + this.library_name + '" is not available');
1511 if(this.debug) this.log('My chart library ' + this.library_name + ' is not available');
1515 if(this.isVisible() === false) {
1516 if(NETDATA.options.debug.visibility || this.debug) this.log('I am not visible');
1520 if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
1521 if(this.debug) this.log('timed force update detecting - allowing this update');
1522 this.current.force_update_at = 0;
1526 if(this.isAutoRefreshed()) {
1527 // allow the first update, even if the page is not visible
1528 if(this.updates_counter && !NETDATA.options.page_is_visible) {
1529 if(NETDATA.options.debug.focus || this.debug) this.log('canBeAutoRefreshed(): page does not have focus');
1533 // options valid only for autoRefresh()
1534 if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
1535 if(NETDATA.globalPanAndZoom.isActive()) {
1536 if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
1537 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I need an update.');
1541 if(this.debug) this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
1547 if(this.debug) this.log('canBeAutoRefreshed(): I have a selection in place.');
1552 if(this.debug) this.log('canBeAutoRefreshed(): I am paused.');
1556 if(now - this.current.last_autorefreshed > this.current.view_update_every) {
1557 if(this.debug) this.log('canBeAutoRefreshed(): It is time to update me.');
1566 chartState.prototype.autoRefresh = function(callback) {
1567 if(this.canBeAutoRefreshed()) {
1568 this.updateChart(callback);
1571 if(typeof callback !== 'undefined')
1576 chartState.prototype._defaultsFromDownloadedChart = function(chart) {
1578 this.chart_url = chart.url;
1579 this.current.view_update_every = chart.update_every * 1000;
1580 this.current.points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1583 // fetch the chart description from the netdata server
1584 chartState.prototype.getChart = function(callback) {
1585 this.chart = NETDATA.chartRegistry.get(this.host, this.id);
1587 this._defaultsFromDownloadedChart(this.chart);
1588 if(typeof callback === 'function') callback();
1591 this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
1592 if(this.debug) this.log('downloading ' + this.chart_url);
1596 url: this.chart_url,
1597 crossDomain: NETDATA.options.crossDomainAjax,
1601 .done(function(chart) {
1602 chart.url = self.chart_url;
1603 chart.data_url = (self.host + chart.data_url);
1604 self._defaultsFromDownloadedChart(chart);
1605 NETDATA.chartRegistry.add(self.host, self.id, chart);
1608 NETDATA.error(404, self.chart_url);
1609 self.error('chart "' + self.id + '" not found on url "' + self.chart_url + '"');
1611 .always(function() {
1612 if(typeof callback === 'function') callback();
1617 // resize the chart to its real dimensions
1618 // as given by the caller
1619 chartState.prototype.sizeChart = function() {
1620 this.element.className += " netdata-container";
1622 if(this.debug) this.log('sizing element');
1625 $(this.element).css('width', this.width);
1628 $(this.element).css('height', this.height);
1630 if(NETDATA.chartDefaults.min_width)
1631 $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
1634 // show a message in the chart
1635 chartState.prototype.message = function(type, msg) {
1636 this.element_message.innerHTML = msg;
1639 // reset the creation datetime
1640 // since we overwrote the whole element
1641 if(this.debug) this.log(msg);
1644 // show an error on the chart and stop it forever
1645 chartState.prototype.error = function(msg) {
1646 this.message('error', this.id + ': ' + msg);
1647 this.enabled = false;
1650 // show a message indicating the chart is loading
1651 chartState.prototype.info = function(msg) {
1652 this.message('info', this.id + ': ' + msg);
1655 chartState.prototype.init = function() {
1656 this.element.innerHTML = '';
1658 this.element_message = document.createElement('div');
1659 this.element_message.className += ' netdata-message';
1660 this.element.appendChild(this.element_message);
1662 this.element_loading = document.createElement('div');
1663 this.element_loading.className += ' netdata-chart-is-loading';
1664 this.element.appendChild(this.element_loading);
1666 if(this.debug) this.log('created');
1669 // make sure the host does not end with /
1670 // all netdata API requests use absolute paths
1671 while(this.host.slice(-1) === '/')
1672 this.host = this.host.substring(0, this.host.length - 1);
1674 // check the requested library is available
1675 // we don't initialize it here - it will be initialized when
1676 // this chart will be first used
1677 if(typeof NETDATA.chartLibraries[this.library_name] === 'undefined') {
1678 NETDATA.error(402, this.library_name);
1679 this.error('chart library "' + this.library_name + '" is not found');
1681 else if(!NETDATA.chartLibraries[this.library_name].enabled) {
1682 NETDATA.error(403, this.library_name);
1683 this.error('chart library "' + this.library_name + '" is not enabled');
1686 this.library = NETDATA.chartLibraries[this.library_name];
1688 // if we need to report the rendering speed
1689 // find the element that needs to be updated
1690 if(this.refresh_dt_element_name)
1691 this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
1693 // the default mode for all charts
1694 this.setMode('auto');
1697 // get or create a chart state, given a DOM element
1698 NETDATA.chartState = function(element) {
1699 var state = $(element).data('netdata-state-object') || null;
1701 state = new chartState(element);
1702 $(element).data('netdata-state-object', state);
1707 // ----------------------------------------------------------------------------------------------------------------
1708 // Library functions
1710 // Load a script without jquery
1711 // This is used to load jquery - after it is loaded, we use jquery
1712 NETDATA._loadjQuery = function(callback) {
1713 if(typeof jQuery === 'undefined') {
1714 if(NETDATA.options.debug.main_loop) console.log('loading ' + NETDATA.jQuery);
1716 var script = document.createElement('script');
1717 script.type = 'text/javascript';
1718 script.async = true;
1719 script.src = NETDATA.jQuery;
1721 // script.onabort = onError;
1722 script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
1723 if(typeof callback === "function")
1724 script.onload = callback;
1726 var s = document.getElementsByTagName('script')[0];
1727 s.parentNode.insertBefore(script, s);
1729 else if(typeof callback === "function")
1733 NETDATA._loadCSS = function(filename) {
1734 var fileref = document.createElement("link");
1735 fileref.setAttribute("rel", "stylesheet");
1736 fileref.setAttribute("type", "text/css");
1737 fileref.setAttribute("href", filename);
1739 if (typeof fileref !== 'undefined')
1740 document.getElementsByTagName("head")[0].appendChild(fileref);
1743 NETDATA.colorHex2Rgb = function(hex) {
1744 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
1745 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1746 hex = hex.replace(shorthandRegex, function(m, r, g, b) {
1747 return r + r + g + g + b + b;
1750 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1752 r: parseInt(result[1], 16),
1753 g: parseInt(result[2], 16),
1754 b: parseInt(result[3], 16)
1758 NETDATA.colorLuminance = function(hex, lum) {
1759 // validate hex string
1760 hex = String(hex).replace(/[^0-9a-f]/gi, '');
1762 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
1766 // convert to decimal and change luminosity
1767 var rgb = "#", c, i;
1768 for (i = 0; i < 3; i++) {
1769 c = parseInt(hex.substr(i*2,2), 16);
1770 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
1771 rgb += ("00"+c).substr(c.length);
1777 NETDATA.guid = function() {
1779 return Math.floor((1 + Math.random()) * 0x10000)
1784 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
1787 NETDATA.zeropad = function(x) {
1788 if(x > -10 && x < 10) return '0' + x.toString();
1789 else return x.toString();
1792 // user function to signal us the DOM has been
1794 NETDATA.updatedDom = function() {
1795 NETDATA.options.updated_dom = true;
1798 NETDATA.ready = function(callback) {
1799 NETDATA.options.readyCallback = callback;
1802 NETDATA.pause = function(callback) {
1803 NETDATA.options.pauseCallback = callback;
1806 NETDATA.unpause = function() {
1807 NETDATA.options.pauseCallback = null;
1808 NETDATA.options.updated_dom = true;
1809 NETDATA.options.pause = false;
1812 // ----------------------------------------------------------------------------------------------------------------
1814 NETDATA.chartRefresherNoParallel = function(index) {
1815 if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
1817 if(NETDATA.options.updated_dom) {
1818 // the dom has been updated
1819 // get the dom parts again
1820 NETDATA.getDomCharts(NETDATA.chartRefresher);
1823 if(index >= NETDATA.options.targets.length) {
1824 if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
1825 NETDATA.options.auto_refresher_fast_weight = 0;
1827 setTimeout(function() {
1828 NETDATA.chartRefresher();
1829 }, NETDATA.options.current.idle_between_loops);
1832 var state = NETDATA.options.targets[index];
1834 if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
1835 if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
1837 state.autoRefresh(function() {
1838 NETDATA.chartRefresherNoParallel(++index);
1842 if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
1843 NETDATA.options.auto_refresher_fast_weight = 0;
1845 setTimeout(function() {
1846 state.autoRefresh(function() {
1847 NETDATA.chartRefresherNoParallel(++index);
1849 }, NETDATA.options.current.idle_between_charts);
1854 NETDATA.chartRefresher_sequencial = function() {
1855 if(NETDATA.options.updated_dom) {
1856 // the dom has been updated
1857 // get the dom parts again
1858 NETDATA.getDomCharts(NETDATA.chartRefresher);
1862 if(NETDATA.options.sequencial.length === 0)
1863 NETDATA.chartRefresher();
1865 var state = NETDATA.options.sequencial.pop();
1866 if(state.library.initialized)
1867 NETDATA.chartRefresher();
1869 state.autoRefresh(NETDATA.chartRefresher_sequencial);
1873 NETDATA.chartRefresher = function() {
1874 if(NETDATA.options.pause) {
1875 // console.log('auto-refresher is paused');
1876 setTimeout(NETDATA.chartRefresher,
1877 NETDATA.options.current.idle_between_loops);
1881 if(typeof NETDATA.options.pauseCallback === 'function') {
1882 // console.log('auto-refresher is calling pauseCallback');
1883 NETDATA.options.pause = true;
1884 NETDATA.options.pauseCallback();
1885 NETDATA.chartRefresher();
1888 if(!NETDATA.options.current.parallel_refresher) {
1889 NETDATA.chartRefresherNoParallel(0);
1893 if(NETDATA.options.updated_dom) {
1894 // the dom has been updated
1895 // get the dom parts again
1896 NETDATA.getDomCharts(NETDATA.chartRefresher);
1900 NETDATA.options.parallel = new Array();
1901 NETDATA.options.sequencial = new Array();
1903 var targets = NETDATA.options.targets;
1904 var len = targets.length;
1906 var state = targets[len];
1908 if(!state.library.initialized)
1909 NETDATA.options.sequencial.push(state);
1911 NETDATA.options.parallel.push(state);
1914 if(NETDATA.options.parallel.length > 0) {
1915 NETDATA.options.parallel_jobs = NETDATA.options.parallel.length;
1917 $(NETDATA.options.parallel).each(function() {
1918 this.autoRefresh(function() {
1919 NETDATA.options.parallel_jobs--;
1920 if(NETDATA.options.parallel_jobs === 0) {
1921 setTimeout(NETDATA.chartRefresher_sequencial,
1922 NETDATA.options.current.idle_between_charts);
1928 setTimeout(NETDATA.chartRefresher_sequencial,
1929 NETDATA.options.current.idle_between_charts);
1933 NETDATA.getDomCharts = function(callback) {
1934 NETDATA.options.updated_dom = false;
1936 var targets = $('div[data-netdata]').filter(':visible');
1938 if(NETDATA.options.debug.main_loop)
1939 console.log('DOM updated - there are ' + targets.length + ' charts on page.');
1941 NETDATA.options.targets = new Array();
1942 var len = targets.length;
1944 // the initialization will take care of sizing
1945 // and the "loading..." message
1946 NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
1949 if(typeof callback === 'function') callback();
1952 // this is the main function - where everything starts
1953 NETDATA.start = function() {
1954 // this should be called only once
1956 NETDATA.options.page_is_visible = true;
1958 $(window).blur(function() {
1959 NETDATA.options.page_is_visible = false;
1960 if(NETDATA.options.debug.focus) console.log('Lost Focus!');
1963 $(window).focus(function() {
1964 NETDATA.options.page_is_visible = true;
1965 if(NETDATA.options.debug.focus) console.log('Focus restored!');
1968 if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
1969 NETDATA.options.page_is_visible = false;
1970 if(NETDATA.options.debug.focus) console.log('Document has no focus!');
1973 NETDATA.getDomCharts(NETDATA.chartRefresher);
1976 // ----------------------------------------------------------------------------------------------------------------
1979 NETDATA.peityInitialize = function(callback) {
1980 if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
1982 url: NETDATA.peity_js,
1987 NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
1990 NETDATA.error(100, NETDATA.peity_js);
1992 .always(function() {
1993 if(typeof callback === "function")
1998 NETDATA.chartLibraries.peity.enabled = false;
1999 if(typeof callback === "function")
2004 NETDATA.peityChartUpdate = function(state, data) {
2005 $(state.peity_instance).html(data.result);
2006 // $(state.element_chart).change() does not accept options
2007 // to pass width and height
2008 //$(state.peity_instance).change();
2009 $(state.peity_instance).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
2012 NETDATA.peityChartCreate = function(state, data) {
2013 state.peity_instance = document.createElement('div');
2014 state.element_chart.appendChild(state.peity_instance);
2016 $(state.peity_instance).html(data.result);
2017 $(state.peity_instance).peity('line', { width: state.chartWidth(), height: state.chartHeight() });
2020 // ----------------------------------------------------------------------------------------------------------------
2023 NETDATA.sparklineInitialize = function(callback) {
2024 if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
2026 url: NETDATA.sparkline_js,
2031 NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
2034 NETDATA.error(100, NETDATA.sparkline_js);
2036 .always(function() {
2037 if(typeof callback === "function")
2042 NETDATA.chartLibraries.sparkline.enabled = false;
2043 if(typeof callback === "function")
2048 NETDATA.sparklineChartUpdate = function(state, data) {
2049 state.sparkline_options.width = state.chartWidth();
2050 state.sparkline_options.height = state.chartHeight();
2052 $(state.element_chart).sparkline(data.result, state.sparkline_options);
2055 NETDATA.sparklineChartCreate = function(state, data) {
2056 var self = $(state.element);
2057 var type = self.data('sparkline-type') || 'line';
2058 var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0];
2059 var fillColor = self.data('sparkline-fillcolor') || (state.chart.chart_type === 'line')?'#FFF':NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance);
2060 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
2061 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
2062 var composite = self.data('sparkline-composite') || undefined;
2063 var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
2064 var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
2065 var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
2066 var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
2067 var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
2068 var spotColor = self.data('sparkline-spotcolor') || undefined;
2069 var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
2070 var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
2071 var spotRadius = self.data('sparkline-spotradius') || undefined;
2072 var valueSpots = self.data('sparkline-valuespots') || undefined;
2073 var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
2074 var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
2075 var lineWidth = self.data('sparkline-linewidth') || undefined;
2076 var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
2077 var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
2078 var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
2079 var xvalues = self.data('sparkline-xvalues') || undefined;
2080 var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
2081 var xvalues = self.data('sparkline-xvalues') || undefined;
2082 var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
2083 var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
2084 var disableInteraction = self.data('sparkline-disableinteraction') || false;
2085 var disableTooltips = self.data('sparkline-disabletooltips') || false;
2086 var disableHighlight = self.data('sparkline-disablehighlight') || false;
2087 var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
2088 var highlightColor = self.data('sparkline-highlightcolor') || undefined;
2089 var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
2090 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
2091 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
2092 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
2093 var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.chart.units;
2094 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
2095 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
2096 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
2097 var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
2098 var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
2099 var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
2100 var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
2101 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
2102 var animatedZooms = self.data('sparkline-animatedzooms') || false;
2104 state.sparkline_options = {
2106 lineColor: lineColor,
2107 fillColor: fillColor,
2108 chartRangeMin: chartRangeMin,
2109 chartRangeMax: chartRangeMax,
2110 composite: composite,
2111 enableTagOptions: enableTagOptions,
2112 tagOptionPrefix: tagOptionPrefix,
2113 tagValuesAttribute: tagValuesAttribute,
2114 disableHiddenCheck: disableHiddenCheck,
2115 defaultPixelsPerValue: defaultPixelsPerValue,
2116 spotColor: spotColor,
2117 minSpotColor: minSpotColor,
2118 maxSpotColor: maxSpotColor,
2119 spotRadius: spotRadius,
2120 valueSpots: valueSpots,
2121 highlightSpotColor: highlightSpotColor,
2122 highlightLineColor: highlightLineColor,
2123 lineWidth: lineWidth,
2124 normalRangeMin: normalRangeMin,
2125 normalRangeMax: normalRangeMax,
2126 drawNormalOnTop: drawNormalOnTop,
2128 chartRangeClip: chartRangeClip,
2129 chartRangeMinX: chartRangeMinX,
2130 chartRangeMaxX: chartRangeMaxX,
2131 disableInteraction: disableInteraction,
2132 disableTooltips: disableTooltips,
2133 disableHighlight: disableHighlight,
2134 highlightLighten: highlightLighten,
2135 highlightColor: highlightColor,
2136 tooltipContainer: tooltipContainer,
2137 tooltipClassname: tooltipClassname,
2138 tooltipChartTitle: state.chart.title,
2139 tooltipFormat: tooltipFormat,
2140 tooltipPrefix: tooltipPrefix,
2141 tooltipSuffix: tooltipSuffix,
2142 tooltipSkipNull: tooltipSkipNull,
2143 tooltipValueLookups: tooltipValueLookups,
2144 tooltipFormatFieldlist: tooltipFormatFieldlist,
2145 tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
2146 numberFormatter: numberFormatter,
2147 numberDigitGroupSep: numberDigitGroupSep,
2148 numberDecimalMark: numberDecimalMark,
2149 numberDigitGroupCount: numberDigitGroupCount,
2150 animatedZooms: animatedZooms,
2151 width: state.chartWidth(),
2152 height: state.chartHeight()
2155 $(state.element_chart).sparkline(data.result, state.sparkline_options);
2158 // ----------------------------------------------------------------------------------------------------------------
2165 NETDATA.dygraphSetSelection = function(state, t) {
2166 if(typeof state.dygraph_instance !== 'undefined') {
2167 var r = state.calculateRowForTime(t);
2169 state.dygraph_instance.setSelection(r);
2171 state.dygraph_instance.clearSelection();
2177 NETDATA.dygraphClearSelection = function(state, t) {
2178 if(typeof state.dygraph_instance !== 'undefined') {
2179 state.dygraph_instance.clearSelection();
2184 NETDATA.dygraphSmoothInitialize = function(callback) {
2186 url: NETDATA.dygraph_smooth_js,
2191 NETDATA.dygraph.smooth = true;
2192 smoothPlotter.smoothing = 0.3;
2194 .always(function() {
2195 if(typeof callback === "function")
2200 NETDATA.dygraphInitialize = function(callback) {
2201 if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
2203 url: NETDATA.dygraph_js,
2208 NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
2209 NETDATA.dygraphSmoothInitialize(callback);
2212 NETDATA.error(100, NETDATA.dygraph_js);
2213 if(typeof callback === "function")
2218 NETDATA.chartLibraries.dygraph.enabled = false;
2219 if(typeof callback === "function")
2224 NETDATA.dygraphChartUpdate = function(state, data) {
2225 var dygraph = state.dygraph_instance;
2227 // when the chart is not visible, and hidden
2228 // if there is a window resize, dygraph detects
2229 // its element size as 0x0.
2230 // this will make it re-appear properly
2233 if(state.current.name === 'pan') {
2234 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() loose update');
2235 dygraph.updateOptions({
2236 file: data.result.data,
2237 labels: data.result.labels,
2238 labelsDivWidth: state.chartWidth() - 70
2242 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartUpdate() strict update');
2243 dygraph.updateOptions({
2244 file: data.result.data,
2245 labels: data.result.labels,
2246 labelsDivWidth: state.chartWidth() - 70,
2253 NETDATA.dygraphChartCreate = function(state, data) {
2254 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphChartCreate()');
2256 var self = $(state.element);
2258 state.dygraph_options = {
2259 colors: self.data('dygraph-colors') || state.chartColors(),
2261 // leave a few pixels empty on the right of the chart
2262 rightGap: self.data('dygraph-rightgap') || 5,
2263 showRangeSelector: self.data('dygraph-showrangeselector') || false,
2264 showRoller: self.data('dygraph-showroller') || false,
2266 title: self.data('dygraph-title') || state.chart.title,
2267 titleHeight: self.data('dygraph-titleheight') || 19,
2269 legend: self.data('dygraph-legend') || NETDATA.chartLibraries.dygraph.legend(state)?'always':'never', // 'onmouseover',
2270 labels: data.result.labels,
2271 labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden,
2272 labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px', 'zIndex': 10000 },
2273 labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70,
2274 labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true,
2275 labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true,
2278 showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true,
2279 hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true,
2281 ylabel: state.chart.units,
2282 yLabelWidth: self.data('dygraph-ylabelwidth') || 12,
2284 // the function to plot the chart
2287 // The width of the lines connecting data points. This can be used to increase the contrast or some graphs.
2288 strokeWidth: self.data('dygraph-strokewidth') || (state.chart.chart_type === 'stacked')?0.0:1.0,
2289 strokePattern: self.data('dygraph-strokepattern') || undefined,
2291 // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated",
2292 // i.e. there is a missing point on either side of it. This also controls the size of those dots.
2293 drawPoints: self.data('dygraph-drawpoints') || false,
2295 // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.
2296 drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true,
2298 connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false,
2299 pointSize: self.data('dygraph-pointsize') || 1,
2301 // enabling this makes the chart with little square lines
2302 stepPlot: self.data('dygraph-stepplot') || false,
2304 // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.
2305 strokeBorderColor: self.data('dygraph-strokebordercolor') || 'white',
2306 strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (state.chart.chart_type === 'stacked')?0.0:0.0,
2308 fillGraph: self.data('dygraph-fillgraph') || (state.chart.chart_type === 'area')?true:false,
2309 fillAlpha: self.data('dygraph-fillalpha') || (state.chart.chart_type === 'stacked')?0.8:0.2,
2310 stackedGraph: self.data('dygraph-stackedgraph') || (state.chart.chart_type === 'stacked')?true:false,
2311 stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none',
2313 drawAxis: self.data('dygraph-drawaxis') || true,
2314 axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10,
2315 axisLineColor: self.data('dygraph-axislinecolor') || '#DDD',
2316 axisLineWidth: self.data('dygraph-axislinewidth') || 0.3,
2318 drawGrid: self.data('dygraph-drawgrid') || true,
2319 drawXGrid: self.data('dygraph-drawxgrid') || undefined,
2320 drawYGrid: self.data('dygraph-drawygrid') || undefined,
2321 gridLinePattern: self.data('dygraph-gridlinepattern') || null,
2322 gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
2323 gridLineColor: self.data('dygraph-gridlinecolor') || '#EEE',
2325 maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8,
2326 sigFigs: self.data('dygraph-sigfigs') || null,
2327 digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2,
2328 valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); },
2330 highlightCircleSize: self.data('dygraph-highlightcirclesize') || 4,
2331 highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 },
2332 highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (state.chart.chart_type === 'stacked')?0.7:0.5,
2334 pointClickCallback: self.data('dygraph-pointclickcallback') || undefined,
2338 ticker: Dygraph.dateTicker,
2339 axisLabelFormatter: function (d, gran) {
2340 return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
2342 valueFormatter: function (ms) {
2343 var d = new Date(ms);
2344 return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
2345 // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
2350 valueFormatter: function (x) {
2351 // return (Math.round(x*100) / 100).toLocaleString();
2352 //return state.legendFormatValue(x);
2358 legendFormatter: function(data) {
2359 var g = data.dygraph;
2361 var elements = state.element_legend_childs;
2363 // if the hidden div is not there
2364 // we are not managing the legend
2365 if(elements.hidden === null) return;
2367 if (typeof data.x === 'undefined') {
2368 state.legendReset();
2371 state.legendSetDate(data.x);
2372 var i = data.series.length;
2374 var series = data.series[i];
2375 if(!series.isVisible) continue;
2376 state.legendSetLabelValue(series.label, series.y);
2382 drawCallback: function(dygraph, is_initial) {
2383 if(state.current.name !== 'auto') {
2384 if(NETDATA.options.debug.dygraph) state.log('dygraphDrawCallback()');
2386 var x_range = dygraph.xAxisRange();
2387 var after = Math.round(x_range[0]);
2388 var before = Math.round(x_range[1]);
2390 state.updateChartPanOrZoom(after, before);
2393 zoomCallback: function(minDate, maxDate, yRanges) {
2394 if(NETDATA.options.debug.dygraph) state.log('dygraphZoomCallback()');
2395 state.globalSelectionSyncStop();
2396 state.globalSelectionSyncDelay();
2397 state.updateChartPanOrZoom(minDate, maxDate);
2399 highlightCallback: function(event, x, points, row, seriesName) {
2400 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphHighlightCallback()');
2403 // there is a bug in dygraph when the chart is zoomed enough
2404 // the time it thinks is selected is wrong
2405 // here we calculate the time t based on the row number selected
2407 var t = state.current.after_ms + row * state.current.view_update_every;
2408 // 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);
2410 state.globalSelectionSync(t);
2412 // fix legend zIndex using the internal structures of dygraph legend module
2413 // this works, but it is a hack!
2414 // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
2416 unhighlightCallback: function(event) {
2417 if(NETDATA.options.debug.dygraph || state.debug) state.log('dygraphUnhighlightCallback()');
2418 state.unpauseChart();
2419 state.globalSelectionSyncStop();
2421 interactionModel : {
2422 mousedown: function(event, dygraph, context) {
2423 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousedown()');
2424 state.globalSelectionSyncStop();
2426 if(NETDATA.options.debug.dygraph) state.log('dygraphMouseDown()');
2428 // Right-click should not initiate a zoom.
2429 if(event.button && event.button === 2) return;
2431 context.initializeMouseDown(event, dygraph, context);
2433 if(event.button && event.button === 1) {
2434 if (event.altKey || event.shiftKey) {
2435 state.setMode('pan');
2436 state.globalSelectionSyncDelay();
2437 Dygraph.startPan(event, dygraph, context);
2440 state.setMode('zoom');
2441 state.globalSelectionSyncDelay();
2442 Dygraph.startZoom(event, dygraph, context);
2446 if (event.altKey || event.shiftKey) {
2447 state.setMode('zoom');
2448 state.globalSelectionSyncDelay();
2449 Dygraph.startZoom(event, dygraph, context);
2452 state.setMode('pan');
2453 state.globalSelectionSyncDelay();
2454 Dygraph.startPan(event, dygraph, context);
2458 mousemove: function(event, dygraph, context) {
2459 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousemove()');
2461 if(context.isPanning) {
2462 state.globalSelectionSyncStop();
2463 state.globalSelectionSyncDelay();
2464 state.setMode('pan');
2465 Dygraph.movePan(event, dygraph, context);
2467 else if(context.isZooming) {
2468 state.globalSelectionSyncStop();
2469 state.globalSelectionSyncDelay();
2470 state.setMode('zoom');
2471 Dygraph.moveZoom(event, dygraph, context);
2474 mouseup: function(event, dygraph, context) {
2475 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mouseup()');
2477 if (context.isPanning) {
2478 state.globalSelectionSyncDelay();
2479 Dygraph.endPan(event, dygraph, context);
2481 else if (context.isZooming) {
2482 state.globalSelectionSyncDelay();
2483 Dygraph.endZoom(event, dygraph, context);
2486 click: function(event, dygraph, context) {
2487 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.click()');
2488 /*Dygraph.cancelEvent(event);*/
2490 dblclick: function(event, dygraph, context) {
2491 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.dblclick()');
2492 state.globalSelectionSyncStop();
2493 NETDATA.globalPanAndZoom.clearMaster();
2496 mousewheel: function(event, dygraph, context) {
2497 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.mousewheel()');
2499 if(event.altKey || event.shiftKey) {
2500 state.globalSelectionSyncStop();
2501 state.globalSelectionSyncDelay();
2503 // http://dygraphs.com/gallery/interaction-api.js
2504 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
2505 var percentage = normal / 25;
2507 var before_old = state.current.before_ms;
2508 var after_old = state.current.after_ms;
2509 var range_old = before_old - after_old;
2511 var range = range_old * ( 1 - percentage );
2512 var dt = Math.round((range_old - range) / 2);
2514 var before = before_old - dt;
2515 var after = after_old + dt;
2517 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());
2519 state.setMode('zoom');
2520 state.updateChartPanOrZoom(after, before);
2523 touchstart: function(event, dygraph, context) {
2524 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchstart()');
2525 state.globalSelectionSyncStop();
2526 state.globalSelectionSyncDelay();
2527 Dygraph.Interaction.startTouch(event, dygraph, context);
2528 context.touchDirections = { x: true, y: false };
2529 state.setMode('zoom');
2531 touchmove: function(event, dygraph, context) {
2532 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchmove()');
2533 //Dygraph.cancelEvent(event);
2534 state.globalSelectionSyncStop();
2535 Dygraph.Interaction.moveTouch(event, dygraph, context);
2537 touchend: function(event, dygraph, context) {
2538 if(NETDATA.options.debug.dygraph || state.debug) state.log('interactionModel.touchend()');
2539 Dygraph.Interaction.endTouch(event, dygraph, context);
2544 if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
2545 state.dygraph_options.drawGrid = false;
2546 state.dygraph_options.drawAxis = false;
2547 state.dygraph_options.title = undefined;
2548 state.dygraph_options.units = undefined;
2549 state.dygraph_options.legend = 'never'; // 'follow'
2550 state.dygraph_options.ylabel = undefined;
2551 state.dygraph_options.yLabelWidth = 0;
2552 state.dygraph_options.labelsDivWidth = 120;
2553 state.dygraph_options.labelsDivStyles.width = '120px';
2554 state.dygraph_options.labelsSeparateLines = true;
2555 state.dygraph_options.highlightCircleSize = 3;
2556 state.dygraph_options.rightGap = 0;
2557 state.dygraph_options.strokeWidth = 1.0;
2559 else if(NETDATA.dygraph.smooth && state.chart.chart_type === 'line') {
2560 // smooth lines patch
2561 state.dygraph_options.plotter = smoothPlotter;
2562 state.dygraph_options.strokeWidth = 1.5;
2567 state.dygraph_instance = new Dygraph(state.element_chart,
2568 data.result.data, state.dygraph_options);
2571 // ----------------------------------------------------------------------------------------------------------------
2574 NETDATA.morrisInitialize = function(callback) {
2575 if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
2577 // morris requires raphael
2578 if(!NETDATA.chartLibraries.raphael.initialized) {
2579 if(NETDATA.chartLibraries.raphael.enabled) {
2580 NETDATA.raphaelInitialize(function() {
2581 NETDATA.morrisInitialize(callback);
2585 NETDATA.chartLibraries.morris.enabled = false;
2586 if(typeof callback === "function")
2591 NETDATA._loadCSS(NETDATA.morris_css);
2594 url: NETDATA.morris_js,
2599 NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
2602 NETDATA.error(100, NETDATA.morris_js);
2604 .always(function() {
2605 if(typeof callback === "function")
2611 NETDATA.chartLibraries.morris.enabled = false;
2612 if(typeof callback === "function")
2617 NETDATA.morrisChartUpdate = function(state, data) {
2618 state.morris_instance.setData(data.result.data);
2621 NETDATA.morrisChartCreate = function(state, data) {
2623 state.morris_options = {
2624 element: state.element_chart_id,
2625 data: data.result.data,
2627 ykeys: data.dimension_names,
2628 labels: data.dimension_names,
2634 continuousLine: false,
2635 behaveLikeLine: false
2638 if(state.chart.chart_type === 'line')
2639 state.morris_instance = new Morris.Line(state.morris_options);
2641 else if(state.chart.chart_type === 'area') {
2642 state.morris_options.behaveLikeLine = true;
2643 state.morris_instance = new Morris.Area(state.morris_options);
2646 state.morris_instance = new Morris.Area(state.morris_options);
2649 // ----------------------------------------------------------------------------------------------------------------
2652 NETDATA.raphaelInitialize = function(callback) {
2653 if(typeof netdataStopRaphael === 'undefined') {
2655 url: NETDATA.raphael_js,
2660 NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
2663 NETDATA.error(100, NETDATA.raphael_js);
2665 .always(function() {
2666 if(typeof callback === "function")
2671 NETDATA.chartLibraries.raphael.enabled = false;
2672 if(typeof callback === "function")
2677 NETDATA.raphaelChartUpdate = function(state, data) {
2678 $(state.element_chart).raphael(data.result, {
2679 width: state.chartWidth(),
2680 height: state.chartHeight()
2684 NETDATA.raphaelChartCreate = function(state, data) {
2685 $(state.element_chart).raphael(data.result, {
2686 width: state.chartWidth(),
2687 height: state.chartHeight()
2691 // ----------------------------------------------------------------------------------------------------------------
2694 NETDATA.googleInitialize = function(callback) {
2695 if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
2697 url: NETDATA.google_js,
2702 NETDATA.registerChartLibrary('google', NETDATA.google_js);
2704 google.load('visualization', '1.1', {
2705 'packages': ['corechart', 'controls'],
2706 'callback': callback
2710 NETDATA.error(100, NETDATA.google_js);
2711 if(typeof callback === "function")
2716 NETDATA.chartLibraries.google.enabled = false;
2717 if(typeof callback === "function")
2722 NETDATA.googleChartUpdate = function(state, data) {
2723 var datatable = new google.visualization.DataTable(data.result);
2724 state.google_instance.draw(datatable, state.google_options);
2727 NETDATA.googleChartCreate = function(state, data) {
2728 var datatable = new google.visualization.DataTable(data.result);
2730 state.google_options = {
2731 // do not set width, height - the chart resizes itself
2732 //width: state.chartWidth(),
2733 //height: state.chartHeight(),
2735 title: state.chart.title,
2738 // title: "Time of Day",
2739 // format:'HH:mm:ss',
2740 viewWindowMode: 'maximized',
2751 title: state.chart.units,
2752 viewWindowMode: 'pretty',
2767 focusTarget: 'category',
2774 titlePosition: 'out',
2785 curveType: 'function',
2790 switch(state.chart.chart_type) {
2792 state.google_options.vAxis.viewWindowMode = 'maximized';
2793 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2797 state.google_options.isStacked = true;
2798 state.google_options.areaOpacity = 0.85;
2799 state.google_options.vAxis.viewWindowMode = 'maximized';
2800 state.google_options.vAxis.minValue = null;
2801 state.google_options.vAxis.maxValue = null;
2802 state.google_instance = new google.visualization.AreaChart(state.element_chart);
2807 state.google_options.lineWidth = 2;
2808 state.google_instance = new google.visualization.LineChart(state.element_chart);
2812 state.google_instance.draw(datatable, state.google_options);
2815 // ----------------------------------------------------------------------------------------------------------------
2818 NETDATA.easypiechartInitialize = function(callback) {
2819 if(typeof netdataStopEasypiechart === 'undefined') {
2821 url: NETDATA.easypiechart_js,
2826 NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
2829 NETDATA.error(100, NETDATA.easypiechart_js);
2831 .always(function() {
2832 if(typeof callback === "function")
2837 NETDATA.chartLibraries.easypiechart.enabled = false;
2838 if(typeof callback === "function")
2843 NETDATA.easypiechartChartUpdate = function(state, data) {
2845 state.easypiechart_instance.update();
2848 NETDATA.easypiechartChartCreate = function(state, data) {
2849 var self = $(state.element);
2854 $(state.element_chart).data('data-percent', pcent);
2855 data.element_chart.innerHTML = value.toString();
2857 state.easypiechart_instance = new EasyPieChart(state.element_chart, {
2858 barColor: self.data('easypiechart-barcolor') || '#ef1e25',
2859 trackColor: self.data('easypiechart-trackcolor') || '#f2f2f2',
2860 scaleColor: self.data('easypiechart-scalecolor') || '#dfe0e0',
2861 scaleLength: self.data('easypiechart-scalelength') || 5,
2862 lineCap: self.data('easypiechart-linecap') || 'round',
2863 lineWidth: self.data('easypiechart-linewidth') || 3,
2864 trackWidth: self.data('easypiechart-trackwidth') || undefined,
2865 size: self.data('easypiechart-size') || Math.min(state.chartWidth(), state.chartHeight()),
2866 rotate: self.data('easypiechart-rotate') || 0,
2867 animate: self.data('easypiechart-rotate') || {duration: 0, enabled: false},
2868 easing: self.data('easypiechart-easing') || undefined
2872 // ----------------------------------------------------------------------------------------------------------------
2873 // Charts Libraries Registration
2875 NETDATA.chartLibraries = {
2877 initialize: NETDATA.dygraphInitialize,
2878 create: NETDATA.dygraphChartCreate,
2879 update: NETDATA.dygraphChartUpdate,
2880 setSelection: NETDATA.dygraphSetSelection,
2881 clearSelection: NETDATA.dygraphClearSelection,
2884 format: function(state) { return 'json'; },
2885 options: function(state) { return 'ms|flip'; },
2886 legend: function(state) {
2887 if(!this.isSparkline(state))
2888 return 'right-side';
2892 autoresize: function(state) { return true; },
2893 max_updates_to_recreate: function(state) { return 5000; },
2894 pixels_per_point: function(state) {
2895 if(!this.isSparkline(state))
2901 isSparkline: function(state) {
2902 if(typeof state.dygraph_sparkline === 'undefined') {
2903 var t = $(state.element).data('dygraph-theme');
2904 if(t === 'sparkline')
2905 state.dygraph_sparkline = true;
2907 state.dygraph_sparkline = false;
2909 return state.dygraph_sparkline;
2913 initialize: NETDATA.sparklineInitialize,
2914 create: NETDATA.sparklineChartCreate,
2915 update: NETDATA.sparklineChartUpdate,
2916 setSelection: function(t) { return true; },
2917 clearSelection: function() { return true; },
2920 format: function(state) { return 'array'; },
2921 options: function(state) { return 'flip|abs'; },
2922 legend: function(state) { return null; },
2923 autoresize: function(state) { return false; },
2924 max_updates_to_recreate: function(state) { return 5000; },
2925 pixels_per_point: function(state) { return 3; }
2928 initialize: NETDATA.peityInitialize,
2929 create: NETDATA.peityChartCreate,
2930 update: NETDATA.peityChartUpdate,
2931 setSelection: function(t) { return true; },
2932 clearSelection: function() { return true; },
2935 format: function(state) { return 'ssvcomma'; },
2936 options: function(state) { return 'null2zero|flip|abs'; },
2937 legend: function(state) { return null; },
2938 autoresize: function(state) { return false; },
2939 max_updates_to_recreate: function(state) { return 5000; },
2940 pixels_per_point: function(state) { return 3; }
2943 initialize: NETDATA.morrisInitialize,
2944 create: NETDATA.morrisChartCreate,
2945 update: NETDATA.morrisChartUpdate,
2946 setSelection: function(t) { return true; },
2947 clearSelection: function() { return true; },
2950 format: function(state) { return 'json'; },
2951 options: function(state) { return 'objectrows|ms'; },
2952 legend: function(state) { return null; },
2953 autoresize: function(state) { return false; },
2954 max_updates_to_recreate: function(state) { return 50; },
2955 pixels_per_point: function(state) { return 15; }
2958 initialize: NETDATA.googleInitialize,
2959 create: NETDATA.googleChartCreate,
2960 update: NETDATA.googleChartUpdate,
2961 setSelection: function(t) { return true; },
2962 clearSelection: function() { return true; },
2965 format: function(state) { return 'datatable'; },
2966 options: function(state) { return ''; },
2967 legend: function(state) { return null; },
2968 autoresize: function(state) { return false; },
2969 max_updates_to_recreate: function(state) { return 300; },
2970 pixels_per_point: function(state) { return 4; }
2973 initialize: NETDATA.raphaelInitialize,
2974 create: NETDATA.raphaelChartCreate,
2975 update: NETDATA.raphaelChartUpdate,
2976 setSelection: function(t) { return true; },
2977 clearSelection: function() { return true; },
2980 format: function(state) { return 'json'; },
2981 options: function(state) { return ''; },
2982 legend: function(state) { return null; },
2983 autoresize: function(state) { return false; },
2984 max_updates_to_recreate: function(state) { return 5000; },
2985 pixels_per_point: function(state) { return 3; }
2988 initialize: NETDATA.easypiechartInitialize,
2989 create: NETDATA.easypiechartChartCreate,
2990 update: NETDATA.easypiechartChartUpdate,
2991 setSelection: function(t) { return true; },
2992 clearSelection: function() { return true; },
2995 format: function(state) { return 'json'; },
2996 options: function(state) { return ''; },
2997 legend: function(state) { return null; },
2998 autoresize: function(state) { return false; },
2999 max_updates_to_recreate: function(state) { return 5000; },
3000 pixels_per_point: function(state) { return 3; }
3004 NETDATA.registerChartLibrary = function(library, url) {
3005 if(NETDATA.options.debug.libraries)
3006 console.log("registering chart library: " + library);
3008 NETDATA.chartLibraries[library].url = url;
3009 NETDATA.chartLibraries[library].initialized = true;
3010 NETDATA.chartLibraries[library].enabled = true;
3013 // ----------------------------------------------------------------------------------------------------------------
3016 NETDATA.requiredJs = [
3017 NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
3018 NETDATA.serverDefault + 'lib/bootstrap.min.js'
3021 NETDATA.loadRequiredJs = function(index, callback) {
3022 if(index >= NETDATA.requiredJs.length) {
3023 if(typeof callback === 'function')
3028 if(NETDATA.options.debug.main_loop) console.log('loading ' + NETDATA.requiredJs[index]);
3030 url: NETDATA.requiredJs[index],
3034 .success(function() {
3035 if(NETDATA.options.debug.main_loop) console.log('loaded ' + NETDATA.requiredJs[index]);
3036 NETDATA.loadRequiredJs(++index, callback);
3039 alert('Cannot load required JS library: ' + NETDATA.requiredJs[index]);
3043 NETDATA.requiredCSS = [
3044 NETDATA.serverDefault + 'css/bootstrap.min.css',
3045 //NETDATA.serverDefault + 'css/bootstrap-theme.min.css',
3046 NETDATA.dashboard_css
3049 NETDATA.loadRequiredCSS = function(index) {
3050 if(index >= NETDATA.requiredCSS.length) {
3051 if(typeof callback === 'function')
3056 if(NETDATA.options.debug.main_loop) console.log('loading ' + NETDATA.requiredCSS[index]);
3057 NETDATA._loadCSS(NETDATA.requiredCSS[index]);
3058 NETDATA.loadRequiredCSS(++index);
3061 NETDATA.errorReset();
3062 NETDATA._loadjQuery(function() {
3063 NETDATA.loadRequiredJs(0, function() {
3064 //NETDATA.chartRegistry.downloadAll(NETDATA.serverDefault, function() {
3065 NETDATA.loadRequiredCSS(0);
3066 // NETDATA._loadCSS(NETDATA.dashboard_css);
3067 if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
3068 if(NETDATA.options.debug.main_loop) console.log('starting chart refresh thread');
3072 if(typeof NETDATA.options.readyCallback === 'function')
3073 NETDATA.options.readyCallback();
3078 // window.NETDATA = NETDATA;
3079 // })(window, document);