@cryptotaxi247 / netdata-1 / commits / 201b4595d

new dashboard; now charts support pan and zoom; dygraphs are also synchronized

Costa Tsaousis (ktsaou) committed Dec 5, 2015 at 22:11 UTC 201b4595dedc61c55ad7008c3e12581e84b36dbd
2 files changed +942 -594
src/rrd2json.c
+9 -6
@@ -442,11 +442,17 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
442 buffer_sprintf(wb, "{\n"
443 " %sid%s: %s%s%s,\n"
444 " %sname%s: %s%s%s,\n"
445 + " %sview_update_every%s: %d,\n"
446 " %supdate_every%s: %d,\n"
447 + " %sfirst_entry_t%s: %u, \n"
448 + " %slast_entry_t%s: %u, \n"
449 " %smin%s: "
450 , kq, kq, sq, r->st->id, sq
451 , kq, kq, sq, r->st->name, sq
452 , kq, kq, r->update_every
453 + , kq, kq, r->st->update_every
454 + , kq, kq, rrdset_first_entry_t(r->st)
455 + , kq, kq, rrdset_last_entry_t(r->st)
456 , kq, kq);
457
458 buffer_rrd_value(wb, r->min);
@@ -1171,6 +1177,8 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
1177
1178 r->group = group;
1179 r->update_every = group * st->update_every;
1180 + r->before = now;
1181 + r->after = now;
1182
1183 long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
1184 for(; !stop_now ; now -= dt, slot--, counter++) {
@@ -1194,10 +1202,6 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
1202
1203 if(unlikely(group_count == 0)) {
1204 group_start_t = now;
1197 - if(unlikely(!r->before)) {
1198 - r->before = group_start_t;
1199 - r->after = group_start_t;
1200 - }
1205 }
1206 group_count++;
1207
@@ -1240,7 +1244,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
1244 if(unlikely(add_this)) {
1245 if(unlikely(!rrdr_line_init(r, group_start_t))) break;
1246
1243 - r->after = group_start_t;
1247 + r->after = now;
1248
1249 calculated_number *cn = rrdr_line_values(r);
1250 uint8_t *co = rrdr_line_options(r);
@@ -1280,7 +1284,6 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
1284 add_this = 0;
1285 }
1286 }
1283 - r->after += group * st->update_every;
1287
1288 rrdr_done(r);
1289 //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
web/dashboard.js
+933 -588
@@ -79,7 +79,7 @@
79 width: '100%', // the chart width
80 height: '100%', // the chart height
81 library: 'dygraph', // the graphing library to use
82 - method: 'average', // the grouping method
82 + method: 'max', // the grouping method
83 before: 0, // panning
84 after: -600, // panning
85 pixels_per_point: 1 // the detail of the chart
@@ -88,16 +88,17 @@
88 NETDATA.options = {
89 targets: null,
90 updated_dom: 1,
91 - last_paused: 0,
91 + auto_refresher_fast_weight: 0,
92 page_is_visible: 1,
93 double_click_ms: 100,
94 - refreshed_stop_until: 0,
94 + auto_refresher_stop_until: 0,
95
96 current: {
97 pixels_per_point: 1,
98 - idle_between_charts: 100,
99 - idle_between_loops: 500,
98 + idle_between_charts: 50,
99 + idle_between_loops: 200,
100 idle_lost_focus: 500,
101 + global_pan_sync_time: 500,
102 fast_render_timeframe: 200 // render continously for these many ms
103 },
104
@@ -148,20 +149,671 @@
149 NETDATA.errorLast.code = 0;
150 NETDATA.errorLast.message = "You are doing fine!";
151 NETDATA.errorLast.datetime = 0;
152 + };
153 +
154 + // ----------------------------------------------------------------------------------------------------------------
155 +
156 + NETDATA.chartRegistry = {
157 + charts: {},
158 +
159 + add: function(host, id, data) {
160 + host = host.replace(/:/g, "_").replace(/\//g, "_");
161 + id = id.replace(/:/g, "_").replace(/\//g, "_");
162 +
163 + if(typeof this.charts[host] == 'undefined')
164 + this.charts[host] = {};
165 +
166 + this.charts[host][id] = data;
167 + },
168 +
169 + get: function(host, id) {
170 + host = host.replace(/:/g, "_").replace(/\//g, "_");
171 + id = id.replace(/:/g, "_").replace(/\//g, "_");
172 +
173 + if(typeof this.charts[host] == 'undefined')
174 + return null;
175 +
176 + if(typeof this.charts[host][id] == 'undefined')
177 + return null;
178 +
179 + return this.charts[host][id];
180 + }
181 + };
182 +
183 + // ----------------------------------------------------------------------------------------------------------------
184 +
185 + NETDATA.globalPanAndZoom = {
186 + seq: 0,
187 + state: null,
188 + before_ms: null,
189 + after_ms: null,
190 + force_before_ms: null,
191 + force_after_ms: null,
192 +
193 + setMaster: function(state, after, before) {
194 + if(this.state && this.state != state) this.state.resetChart();
195 +
196 + this.state = state;
197 + this.seq = new Date().getTime();
198 + this.force_after_ms = after;
199 + this.force_before_ms = before;
200 + },
201 + clearMaster: function() {
202 + if(this.state) {
203 + var state = this.state;
204 + this.state = null; // prevent infinite recursion
205 + this.seq = 0;
206 + state.resetChart();
207 + NETDATA.options.auto_refresher_stop_until = 0;
208 + }
209 + else {
210 + this.state = null;
211 + this.seq = 0;
212 + }
213 + },
214 + shouldBeAutoRefreshed: function(state) {
215 + if(!this.state || !this.seq)
216 + return false;
217 +
218 + if(state.follows_global == this.seq)
219 + return false;
220 +
221 + return true;
222 + }
223 }
224
153 - NETDATA.messageInABox = function(element, message) {
225 + // ----------------------------------------------------------------------------------------------------------------
226 + // Our state object, where all per-chart values are stored
227 +
228 + NETDATA.chartInitialState = function(element) {
229 self = $(element);
230
156 - bgcolor = ""
157 - if(NETDATA.options.debug.show_boxes)
158 - bgcolor = " background-color: lightgrey;";
231 + var state = {
232 + uuid: NETDATA.guid(), // GUID - a unique identifier for the chart
233 + id: self.data('netdata'), // string - the name of chart
234 +
235 + // the user given dimensions of the element
236 + width: self.data('width') || NETDATA.chartDefaults.width,
237 + height: self.data('height') || NETDATA.chartDefaults.height,
238 +
239 + // these are calculated every time the chart is refreshed
240 + calculated_width: 0,
241 + calculated_height: 0,
242 +
243 + // string - the netdata server URL, without any path
244 + host: self.data('host') || NETDATA.chartDefaults.host,
245 +
246 + // string - the grouping method requested by the user
247 + method: self.data('method') || NETDATA.chartDefaults.method,
248 +
249 + // the time-range requested by the user
250 + after: self.data('after') || NETDATA.chartDefaults.after,
251 + before: self.data('before') || NETDATA.chartDefaults.before,
252 +
253 + // the pixels per point requested by the user
254 + pixels_per_point: 1,
255 + points: self.data('points') || null,
256 +
257 + // the dimensions requested by the user
258 + dimensions: self.data('dimensions') || null,
259 +
260 + // the chart library requested by the user
261 + library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
262 + library: null, // object - the chart library used
263 +
264 + element: element, // the element it is linked to
265 +
266 + chart_url: null, // string - the url to download chart info
267 + chart: null, // object - the chart as downloaded from the server
268 +
269 + downloaded_ms: 0, // milliseconds - the timestamp we downloaded the chart
270 + created_ms: 0, // boolean - the timestamp the chart was created
271 + validated: false, // boolean - has the chart been validated?
272 + enabled: true, // boolean - is the chart enabled for refresh?
273 + paused: false, // boolean - is the chart paused for any reason?
274 + debug: false,
275 + updates_counter: 0, // numeric - the number of refreshes made so far
276 +
277 + follows_global: 0,
278 +
279 + mode: null, // auto, pan, zoom
280 + auto: {
281 + name: 'auto',
282 + autorefresh: true,
283 + url: 'invalid://', // string - the last url used to update the chart
284 + last_updated_ms: 0, // milliseconds - the timestamp of last refresh
285 + view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
286 + after_ms: 0, // milliseconds - the first timestamp of the data
287 + before_ms: 0, // milliseconds - the last timestamp of the data
288 + points: 0, // number - the number of points in the data
289 + data: null, // the last downloaded data
290 + force_before_ms: null,
291 + force_after_ms: null,
292 + requested_before_ms: null,
293 + requested_after_ms: null,
294 + first_entry_ms: null,
295 + last_entry_ms: null,
296 + },
297 + pan: {
298 + name: 'pan',
299 + autorefresh: false,
300 + url: 'invalid://', // string - the last url used to update the chart
301 + last_updated_ms: 0, // milliseconds - the timestamp of last refresh
302 + view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
303 + after_ms: 0, // milliseconds - the first timestamp of the data
304 + before_ms: 0, // milliseconds - the last timestamp of the data
305 + points: 0, // number - the number of points in the data
306 + data: null, // the last downloaded data
307 + force_before_ms: null,
308 + force_after_ms: null,
309 + requested_before_ms: null,
310 + requested_after_ms: null,
311 + first_entry_ms: null,
312 + last_entry_ms: null,
313 + },
314 + zoom: {
315 + name: 'zoom',
316 + autorefresh: false,
317 + url: 'invalid://', // string - the last url used to update the chart
318 + last_updated_ms: 0, // milliseconds - the timestamp of last refresh
319 + view_update_every: 0, // milliseconds - the minimum acceptable refresh duration
320 + after_ms: 0, // milliseconds - the first timestamp of the data
321 + before_ms: 0, // milliseconds - the last timestamp of the data
322 + points: 0, // number - the number of points in the data
323 + data: null, // the last downloaded data
324 + force_before_ms: null,
325 + force_after_ms: null,
326 + requested_before_ms: null,
327 + requested_after_ms: null,
328 + first_entry_ms: null,
329 + last_entry_ms: null,
330 + },
331 + refresh_dt_ms: 0, // milliseconds - the time the last refresh took
332 + refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
333 + refresh_dt_element: null,
334 +
335 + log(msg) {
336 + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
337 + },
338 +
339 + setSelection: function(t) {
340 + if(typeof this.library.setSelection == 'function')
341 + return this.library.setSelection(this, t);
342 + else
343 + return false;
344 + },
345 +
346 + clearSelection: function() {
347 + if(typeof this.library.clearSelection == 'function')
348 + return this.library.clearSelection(this);
349 + else
350 + return false;
351 + },
352 +
353 + timeIsVisible: function(t) {
354 + if(t >= this.mode.after_ms && t <= this.mode.before_ms)
355 + return true;
356 + return false;
357 + },
358 +
359 + calculateRowForTime: function(t) {
360 + if(!this.timeIsVisible(t)) return -1;
361 +
362 + var r = Math.floor((t - this.mode.after_ms) / this.mode.view_update_every);
363 + // console.log(this.mode.data);
364 +
365 + return r;
366 + },
367 +
368 + pauseChart: function() {
369 + this.paused = true;
370 + },
371 +
372 + unpauseChart: function() {
373 + this.paused = false;
374 + },
375 +
376 + resetChart: function() {
377 + if(NETDATA.globalPanAndZoom.state == this)
378 + NETDATA.globalPanAndZoom.clearMaster();
379
160 - element.innerHTML = '<div style="font-size: xx-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
161 - + message
162 - + '</small></div>';
380 + if(state.mode.name != 'auto')
381 + this.setMode('auto');
382 +
383 + this.mode.force_before_ms = null;
384 + this.mode.force_after_ms = null;
385 + this.mode.last_updated_ms = 0;
386 + this.follows_global = 0;
387 + this.paused = false;
388 + this.enabled = true;
389 + this.debug = false;
390 +
391 + state.updateChart();
392 + },
393 +
394 + setMode: function(m) {
395 + if(this.mode) {
396 + if(this.mode.name == m) return;
397 +
398 + this[m].url = this.mode.url;
399 + this[m].last_updated_ms = this.mode.last_updated_ms;
400 + this[m].view_update_every = this.mode.view_update_every;
401 + this[m].after_ms = this.mode.after_ms;
402 + this[m].before_ms = this.mode.before_ms;
403 + this[m].points = this.mode.points;
404 + this[m].data = this.mode.data;
405 + this[m].requested_before_ms = this.mode.requested_before_ms;
406 + this[m].requested_after_ms = this.mode.requested_after_ms;
407 + this[m].first_entry_ms = this.mode.first_entry_ms;
408 + this[m].last_entry_ms = this.mode.last_entry_ms;
409 + }
410 +
411 + if(m == 'auto')
412 + this.mode = this.auto;
413 + else if(m == 'pan')
414 + this.mode = this.pan;
415 + else if(m == 'zoom')
416 + this.mode = this.zoom;
417 + else
418 + this.mode = this.auto;
419 +
420 + this.mode.force_before_ms = null;
421 + this.mode.force_after_ms = null;
422 +
423 + if(this.debug) this.log('mode set to ' + this.mode.name);
424 + },
425 +
426 + _minPanOrZoomStep: function() {
427 + return (((this.mode.before_ms - this.mode.after_ms) / this.mode.points) * ((this.mode.points * 5 / 100) + 1) );
428 + // return this.mode.view_update_every * 10;
429 + },
430 +
431 + _shouldBeMoved: function(old_after, old_before, new_after, new_before) {
432 + var dt_after = Math.abs(old_after - new_after);
433 + var dt_before = Math.abs(old_before - new_before);
434 + var old_range = old_before - old_after;
435 +
436 + var new_range = new_before - new_after;
437 + var dt = Math.abs(old_range - new_range);
438 + var step = Math.max(dt_after, dt_before, dt);
439 +
440 + var min_step = this._minPanOrZoomStep();
441 + if(new_range < old_range && new_range / this.calculated_width < 100) {
442 + 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.calculated_width / 1000).toString() + ': TOO SMALL RANGE');
443 + return false;
444 + }
445 +
446 + if(step >= min_step) {
447 + 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');
448 + return true;
449 + }
450 + else {
451 + 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');
452 + return false;
453 + }
454 + },
455 +
456 + updateChartPanOrZoom: function(after, before, callback) {
457 + var move = false;
458 +
459 + if(this.mode.name == 'auto') {
460 + if(this.debug) this.log('updateChartPanOrZoom(): caller did not set proper mode');
461 + this.setMode('pan');
462 + }
463 +
464 + if(!this.mode.force_after_ms || !this.mode.force_before_ms) {
465 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): INIT');
466 + move = true;
467 + }
468 + else if(this._shouldBeMoved(this.mode.force_after_ms, this.mode.force_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
469 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): FORCE CHANGE from ' + (this.mode.force_after_ms / 1000).toString() + ' - ' + (this.mode.force_before_ms / 1000).toString());
470 + move = true;
471 + }
472 + else if(this._shouldBeMoved(this.mode.requested_after_ms, this.mode.requested_before_ms, after, before) && this._shouldBeMoved(this.mode.after_ms, this.mode.before_ms, after, before)) {
473 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): REQUESTED CHANGE from ' + (this.mode.requested_after_ms / 1000).toString() + ' - ' + (this.mode.requested_before_ms / 1000).toString());
474 + move = true;
475 + }
476 +
477 + if(move) {
478 + var now = new Date().getTime();
479 + NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
480 + NETDATA.globalPanAndZoom.setMaster(this, after, before);
481 +
482 + this.mode.force_after_ms = after;
483 + this.mode.force_before_ms = before;
484 + this.updateChart(callback);
485 + return true;
486 + }
487 +
488 + if(this.debug) this.log('updateChartPanOrZoom(' + (after / 1000).toString() + ' - ' + (before / 1000).toString() + '): IGNORE');
489 + if(typeof callback != 'undefined') callback();
490 + return false;
491 + },
492 +
493 + updateChart: function(callback) {
494 + if(!this.library || !this.library.enabled) {
495 + this.error('chart library "' + this.library_name + '" is not enabled');
496 + if(typeof callback != 'undefined') callback();
497 + return;
498 + }
499 +
500 + if(!this.enabled) {
501 + if(this.debug) this.error('chart "' + this.id + '" is not enabled');
502 + if(typeof callback != 'undefined') callback();
503 + return;
504 + }
505 +
506 + if(!self.visible(true)) {
507 + if(NETDATA.options.debug.visibility || this.debug) this.log('is not visible');
508 + if(typeof callback != 'undefined') callback();
509 + return;
510 + }
511 + if(!this.chart) {
512 + var this_state_object = this;
513 + this.getChart(function() { this_state_object.updateChart(callback); });
514 + return;
515 + }
516 +
517 + if(!this.library.initialized) {
518 + var this_state_object = this;
519 + this.library.initialize(function() { this_state_object.updateChart(callback); });
520 + return;
521 + }
522 +
523 + this.clearSelection();
524 + this.chartURL();
525 + if(this.debug) this.log('updating from ' + this.mode.url);
526 +
527 + var this_state_object = this;
528 + $.ajax( {
529 + url: this_state_object.mode.url,
530 + crossDomain: true
531 + })
532 + .then(function(data) {
533 + if(this_state_object.debug) this_state_object.log('got data from netdata server');
534 + this_state_object.mode.data = data;
535 +
536 + var started = new Date().getTime();
537 +
538 + // if the result is JSON, find the latest update-every
539 + if(typeof data == 'object' && this_state_object.library.jsonWrapper) {
540 + if(!this_state_object.follows_global && typeof data.view_update_every != 'undefined')
541 + this_state_object.mode.view_update_every = data.view_update_every * 1000;
542 +
543 + if(typeof data.after != 'undefined')
544 + this_state_object.mode.after_ms = data.after * 1000;
545 +
546 + if(typeof data.before != 'undefined')
547 + this_state_object.mode.before_ms = data.before * 1000;
548 +
549 + if(typeof data.first_entry_t != 'undefined')
550 + this_state_object.mode.first_entry_ms = data.first_entry_t * 1000;
551 +
552 + if(typeof data.last_entry_t != 'undefined')
553 + this_state_object.mode.last_entry_ms = data.last_entry_t * 1000;
554 +
555 + if(typeof data.points != 'undefined')
556 + this_state_object.mode.points = data.points;
557 +
558 + data.state = this_state_object;
559 + }
560 +
561 + this_state_object.updates_counter++;
562 +
563 + if(this_state_object.debug) {
564 + this_state_object.log('UPDATE No ' + this_state_object.updates_counter + ' COMPLETED');
565 +
566 + if(this_state_object.mode.force_after_ms)
567 + this_state_object.log('STATUS: forced : ' + (this_state_object.mode.force_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.force_before_ms / 1000).toString());
568 + else
569 + this_state_object.log('STATUS: forced: unset');
570 +
571 + this_state_object.log('STATUS: requested: ' + (this_state_object.mode.requested_after_ms / 1000).toString() + ' - ' + (this_state_object.mode.requested_before_ms / 1000).toString());
572 + this_state_object.log('STATUS: rendered : ' + (this_state_object.mode.after_ms / 1000).toString() + ' - ' + (this_state_object.mode.before_ms / 1000).toString());
573 + this_state_object.log('STATUS: points : ' + (this_state_object.mode.points).toString() + ', min step: ' + (this_state_object._minPanOrZoomStep() / 1000).toString());
574 + }
575
164 - self.data('chart-created', false);
576 + if(this_state_object.created_ms) {
577 + if(this_state_object.debug) this_state_object.log('updating chart...');
578 +
579 + if(NETDATA.options.debug.chart_errors) {
580 + this_state_object.library.update(this_state_object.element, data);
581 + }
582 + else {
583 + try {
584 + this_state_object.library.update(this_state_object.element, data);
585 + }
586 + catch(err) {
587 + this_state_object.error('chart "' + state.id + '" failed to be updated as ' + state.library_name);
588 + }
589 + }
590 + }
591 + else {
592 + if(this_state_object.debug) this_state_object.log('creating chart...');
593 +
594 + if(NETDATA.options.debug.chart_errors) {
595 + this_state_object.library.create(this_state_object.element, data);
596 + this_state_object.created_ms = new Date().getTime();
597 + }
598 + else {
599 + try {
600 + this_state_object.library.create(this_state_object.element, data);
601 + this_state_object.created_ms = new Date().getTime();
602 + }
603 + catch(err) {
604 + this_state_object.error('chart "' + state.id + '" failed to be created as ' + state.library_name);
605 + }
606 + }
607 + }
608 +
609 + // update the performance counters
610 + this_state_object.mode.last_updated_ms = new Date().getTime();
611 + this_state_object.refresh_dt_ms = this_state_object.mode.last_updated_ms - started;
612 + NETDATA.options.auto_refresher_fast_weight += this_state_object.refresh_dt_ms;
613 +
614 + if(this_state_object.refresh_dt_element)
615 + this_state_object.refresh_dt_element.innerHTML = this_state_object.refresh_dt_ms.toString();
616 + })
617 + .fail(function() {
618 + this_state_object.error('cannot download chart from ' + this_state_object.mode.url);
619 + })
620 + .always(function() {
621 + if(typeof callback == 'function') callback();
622 + });
623 + },
624 +
625 + chartURL: function() {
626 + this.calculated_width = self.width();
627 + this.calculated_height = self.height();
628 +
629 + var before;
630 + var after;
631 + if(NETDATA.globalPanAndZoom.state) {
632 + after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
633 + before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
634 + this.follows_global = NETDATA.globalPanAndZoom.seq;
635 + }
636 + else {
637 + before = this.mode.force_before_ms != null ? Math.round(this.mode.force_before_ms / 1000) : this.before;
638 + after = this.mode.force_after_ms != null ? Math.round(this.mode.force_after_ms / 1000) : this.after;
639 + this.follows_global = 0;
640 + }
641 +
642 + this.mode.requested_after_ms = after * 1000;
643 + this.mode.requested_before_ms = before * 1000;
644 +
645 + // force an options provided detail
646 + var pixels_per_point = this.pixels_per_point;
647 + if(pixels_per_point < NETDATA.options.current.pixels_per_point)
648 + pixels_per_point = NETDATA.options.current.pixels_per_point
649 +
650 + this.mode.points = this.points || Math.round(this.calculated_width / pixels_per_point);
651 +
652 + // build the data URL
653 + this.mode.url = this.chart.data_url;
654 + this.mode.url += "&format=" + this.library.format;
655 + this.mode.url += "&points=" + this.mode.points.toString();
656 + this.mode.url += "&group=" + this.method;
657 + this.mode.url += "&options=" + this.library.options;
658 + if(this.library.jsonWrapper) this.mode.url += '|jsonwrap';
659 +
660 + if(after)
661 + this.mode.url += "&after=" + after.toString();
662 +
663 + if(before)
664 + this.mode.url += "&before=" + before.toString();
665 +
666 + if(this.dimensions)
667 + this.mode.url += "&dimensions=" + this.dimensions;
668 +
669 + if(NETDATA.options.debug.chart_data_url) this.log('chartURL(): ' + this.mode.url + ' WxH:' + this.calculated_width + 'x' + this.calculated_height + ' points: ' + this.mode.points + ' library: ' + this.library_name);
670 + },
671 +
672 + canBeAutoRefreshed: function(auto_refresher) {
673 + if(this.mode.autorefresh) {
674 + var now = new Date().getTime();
675 +
676 + if(this.updates_counter && !NETDATA.options.page_is_visible) {
677 + if(NETDATA.options.debug.focus || this.debug) this.log('page does not have focus');
678 + return false;
679 + }
680 +
681 + if(!auto_refresher) return true;
682 +
683 + if(!self.visible(true)) return false;
684 +
685 + // options valid only for autoRefresh()
686 + if(NETDATA.options.auto_refresher_stop_until == 0 || NETDATA.options.auto_refresher_stop_until < now) {
687 + if(NETDATA.globalPanAndZoom.state) {
688 + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this))
689 + return true;
690 + else
691 + return false;
692 + }
693 +
694 + if(this.paused) return false;
695 +
696 + if(now - this.mode.last_updated_ms > this.mode.view_update_every)
697 + return true;
698 + }
699 + }
700 +
701 + return false;
702 + },
703 +
704 + autoRefresh: function(callback) {
705 + if(this.canBeAutoRefreshed(true))
706 + this.updateChart(callback);
707 + else if(typeof callback != 'undefined')
708 + callback();
709 + },
710 +
711 + // fetch the chart description from the netdata server
712 + getChart: function(callback) {
713 + this.chart = NETDATA.chartRegistry.get(this.host, this.id);
714 + if(this.chart) {
715 + if(typeof callback == 'function') callback();
716 + }
717 + else {
718 + this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
719 + if(this.debug) this.log('downloading ' + this.chart_url);
720 + this_state_object = this;
721 +
722 + $.ajax( {
723 + url: this.chart_url,
724 + crossDomain: true
725 + })
726 + .done(function(chart) {
727 + chart.data_url = (this_state_object.host + chart.data_url);
728 + this_state_object.chart = chart;
729 + this_state_object.mode.view_update_every = chart.update_every * 1000;
730 + this_state_object.mode.points = Math.round(self.width() / (chart.update_every / 1000));
731 +
732 + chart.url = this_state_object.chart_url;
733 + NETDATA.chartRegistry.add(this_state_object.host, this_state_object.id, chart);
734 + })
735 + .fail(function() {
736 + NETDATA.error(404, this_state_object.chart_url);
737 + this_state_object.error('chart "' + this_state_object.id + '" not found on url "' + this_state_object.chart_url + '"');
738 + })
739 + .always(function() {
740 + if(typeof callback == 'function') callback();
741 + });
742 + }
743 + },
744 +
745 + // resize the chart to its real dimensions
746 + // as given by the caller
747 + sizeChart: function() {
748 + if(this.debug) this.log('sizing element');
749 + self.css('width', this.width)
750 + .css('height', this.height)
751 + .css('display', 'inline-block')
752 + .css('overflow', 'hidden');
753 + },
754 +
755 + // show a message in the chart
756 + message: function(msg) {
757 + var bgcolor = ""
758 + if(NETDATA.options.debug.show_boxes)
759 + bgcolor = " background-color: lightgrey;";
760 +
761 + this.element.innerHTML = '<div style="font-size: x-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
762 + + msg
763 + + '</small></div>';
764 +
765 + // reset the creation datetime
766 + // since we overwrote the whole element
767 + this.created_ms = 0
768 + if(this.debug) this.log(msg);
769 + },
770 +
771 + // show an error on the chart and stop it forever
772 + error: function(msg) {
773 + this.message(msg);
774 + this.enabled = false;
775 + },
776 +
777 + // show a message indicating the chart is loading
778 + loading: function() {
779 + this.message('chart ' + this.id + ' is loading...');
780 + }
781 + };
782 +
783 + if(state.debug) state.log('created');
784 + state.sizeChart();
785 + state.loading();
786 +
787 + if(typeof NETDATA.chartLibraries[state.library_name] == 'undefined') {
788 + NETDATA.error(402, state.library_name);
789 + state.error('chart library "' + state.library_name + '" is not found');
790 + }
791 + else if(!NETDATA.chartLibraries[state.library_name].enabled) {
792 + NETDATA.error(403, state.library_name);
793 + state.error('chart library "' + state.library_name + '" is not enabled');
794 + }
795 + else {
796 + state.library = NETDATA.chartLibraries[state.library_name];
797 + state.pixels_per_point = self.data('pixels-per-point') || state.library.pixels_per_point;
798 + }
799 +
800 + if(state.refresh_dt_element_name)
801 + state.refresh_dt_element = document.getElementById(state.refresh_dt_element_name) || null;
802 +
803 + state.setMode('auto');
804 +
805 + return state;
806 + }
807 +
808 + // get or create a chart state, given a DOM element
809 + NETDATA.chartState = function(element) {
810 + self = $(element);
811 + var state = self.data('state') || null;
812 + if(!state) {
813 + state = NETDATA.chartInitialState(element);
814 + self.data('state', state);
815 + }
816 + return state;
817 }
818
819 // ----------------------------------------------------------------------------------------------------------------
@@ -217,25 +869,6 @@
869 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
870 }
871
220 - // this is the main function - where everything starts
221 - NETDATA.init = function() {
222 - // this should be called only once
223 -
224 - $(window).blur(function() {
225 - NETDATA.options.page_is_visible = 0;
226 - if(NETDATA.options.debug.focus) console.log('Lost Focus!');
227 - });
228 -
229 - $(window).focus(function() {
230 - NETDATA.options.page_is_visible = 1;
231 - if(NETDATA.options.debug.focus) console.log('Focus restored!');
232 - });
233 -
234 - NETDATA.getDomCharts(function() {
235 - NETDATA.chartRefresher(0);
236 - });
237 - }
238 -
872 // user function to signal us the DOM has been
873 // updated.
874 NETDATA.updatedDom = function() {
@@ -244,201 +877,93 @@
877
878 // ----------------------------------------------------------------------------------------------------------------
879
247 - NETDATA.generateChartDataURL = function() {
248 - self = $(this);
249 -
250 - var chart = self.data('chart');
251 - var host = self.data('host') || NETDATA.chartDefaults.host;
252 - var width = self.width();
253 - var height = self.height();
254 - var method = self.data('method') || NETDATA.chartDefaults.method;
255 - var after = self.data('after') || NETDATA.chartDefaults.after;
256 - var before = self.data('before') || NETDATA.chartDefaults.before;
257 - var library = self.data('chart-library') || NETDATA.chartDefaults.library;
258 - var dimensions = self.data('dimensions') || null;
259 - var pixels_per_point = self.data('pixels-per-point') || NETDATA.chartLibraries[library].pixels_per_point;
260 -
261 - if(self.data('chart-panning') || self.data('chart-zooming')) {
262 - before = self.data('panning-before') || 0;
263 - after = self.data('panning-after') || 0;
264 - }
265 -
266 - // force an options provided detail
267 - if(pixels_per_point < NETDATA.options.current.pixels_per_point)
268 - pixels_per_point = NETDATA.options.current.pixels_per_point
269 -
270 - var points = self.data('points') || Math.round(width / pixels_per_point);
271 - var format = self.data('format') || NETDATA.chartLibraries[library].format;
272 - var options = self.data('options') || NETDATA.chartLibraries[library].options;
273 -
274 - // build the data URL
275 - var url = host + chart.data_url;
276 - url += "&format=" + format;
277 - url += "&points=" + points.toString();
278 - url += "&group=" + method;
279 - url += "&options=" + options;
280 - if(NETDATA.chartLibraries[library].jsonWrapper) url += '|jsonwrap';
281 -
282 - if(after)
283 - url += "&after=" + after.toString();
284 -
285 - if(before)
286 - url += "&before=" + before.toString();
287 -
288 - if(dimensions)
289 - url += "&dimensions=" + dimensions;
290 -
291 - self.data('calculated-width', width)
292 - .data('calculated-height', height)
293 - .data('calculated-points', points)
294 - .data('calculated-options', options)
295 - .data('calculated-url', url);
880 + NETDATA.chartRefresher = function(index) {
881 + // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
882
297 - if(NETDATA.options.debug.chart_data_url) console.log('generateChartDataURL(): ' + url + ' WxH:' + width + 'x' + height + ' points: ' + points + ' library: ' + library);
298 - return url;
299 - }
883 + if(NETDATA.options.updated_dom) {
884 + // the dom has been updated
885 + // get the dom parts again
886 + NETDATA.getDomCharts(function() {
887 + NETDATA.chartRefresher(0);
888 + });
889
301 - NETDATA.validateDomCharts = function(targets, index, callback) {
302 - if(NETDATA.options.debug.main_loop) console.log('validateDomCharts() working on ' + index);
890 + return;
891 + }
892
304 - var target = targets.get(index);
893 + var target = NETDATA.options.targets.get(index);
894 if(target == null) {
306 - if(NETDATA.options.debug.main_loop) console.log('validateDomCharts(): all ' + (index - 1) + ' charts parsed.');
307 - if(typeof callback == 'function') callback();
308 - }
895 + if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
896 + NETDATA.options.auto_refresher_fast_weight = 0;
897 +
898 + setTimeout(function() {
899 + NETDATA.chartRefresher(0);
900 + }, NETDATA.options.current.idle_between_loops);
901 + }
902 else {
310 - var self = $(target);
311 - if(!self.data('prepared')) {
312 - self.data('prepared', true)
313 - .data('updated', 0)
314 - .data('chart-created', false)
315 - .data('refresher-enabled', false)
316 - .data('chart-panning', false)
317 - .data('chart-zooming', false)
318 - .data('chart-paused', false)
319 - .data('panning-before', 0)
320 - .data('panning-after', 0);
321 -
322 - var id = self.data('netdata');
323 - var host = self.data('host') || NETDATA.chartDefaults.host;
324 - var library = self.data('chart-library') || NETDATA.chartDefaults.library;
325 -
326 - if(NETDATA.options.debug.main_loop) console.log('validateDomCharts() parsing ' + id + ' of type ' + library);
327 -
328 - if(typeof NETDATA.chartLibraries[library] == 'undefined') {
329 - NETDATA.error(402, library);
330 - NETDATA.messageInABox(target, 'chart library "' + library + '" is not found');
331 - NETDATA.validateDomCharts(targets, ++index, callback);
332 - }
333 - else if(!NETDATA.chartLibraries[library].enabled) {
334 - NETDATA.error(403, library);
335 - NETDATA.messageInABox(target, 'chart library "' + library + '" is not enabled');
336 - NETDATA.validateDomCharts(targets, ++index, callback);
337 - }
338 - else if(!NETDATA.chartLibraries[library].initialized) {
339 - self.data('prepared', false);
340 - NETDATA.chartLibraries[library].initialize(function() {
341 - NETDATA.validateDomCharts(targets, index, callback);
342 - });
343 - }
344 - else {
345 - var url = host + "/api/v1/chart?chart=" + id;
903 + var state = NETDATA.chartState(target);
904
347 - $.ajax( {
348 - url: url,
349 - crossDomain: true
350 - })
351 - .done(function(chart) {
352 - self.data('chart', chart)
353 - .data('update-every', chart.update_every * 1000)
354 - .data('refresher-enabled', true)
355 - .data('host', host)
356 - .data('chart-library', library);
357 - })
358 - .fail(function() {
359 - NETDATA.error(404, url);
360 - NETDATA.messageInABox(target, 'chart "' + id + '" not found on url "' + url + '"');
361 - })
362 - .always(function() {
363 - NETDATA.validateDomCharts(targets, ++index, callback);
364 - });
365 - }
905 + if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
906 + if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
907 +
908 + state.autoRefresh(function() {
909 + NETDATA.chartRefresher(++index);
910 + }, false);
911 }
912 else {
368 - NETDATA.validateDomCharts(targets, ++index, callback);
913 + if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
914 + NETDATA.options.auto_refresher_fast_weight = 0;
915 +
916 + setTimeout(function() {
917 + state.autoRefresh(function() {
918 + NETDATA.chartRefresher(++index);
919 + }, false);
920 + }, NETDATA.options.current.idle_between_charts);
921 }
922 }
923 }
924
373 - NETDATA.sizeDomCharts = function(targets, index, callback) {
374 - // this is used to quickly size all charts to their size
375 -
376 - if(NETDATA.options.debug.main_loop) console.log('sizeDomCharts() working on ' + index);
925 + NETDATA.getDomCharts = function(callback) {
926 + NETDATA.options.updated_dom = 0;
927
378 - var target = targets.get(index);
379 - if(target == null) {
380 - if(NETDATA.options.debug.main_loop) console.log('sizeDomCharts(): all ' + (index - 1) + ' charts sized.');
381 - if(typeof callback == 'function') callback();
382 - }
383 - else {
384 - var self = $(target);
928 + NETDATA.options.targets = $('div[data-netdata]').filter(':visible');
929
386 - var id = self.data('netdata');
387 - var width = self.data('width') || NETDATA.chartDefaults.width;
388 - var height = self.data('height') || NETDATA.chartDefaults.height;
930 + if(NETDATA.options.debug.main_loop)
931 + console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
932
390 - self.css('width', width)
391 - .css('height', height)
392 - .css('display', 'inline-block')
393 - .css('overflow', 'hidden');
933 + // we need to re-size all the charts quickly
934 + // before making any external calls
935 + $.each(NETDATA.options.targets, function(i, target) {
936 + // the initialization will take care of sizing
937 + // and the "loading..." message
938 + var state = NETDATA.chartState(target);
939 + });
940
395 - NETDATA.messageInABox(target, 'chart "' + id + '" is loading...');
396 - NETDATA.sizeDomCharts(targets, ++index, callback);
397 - }
941 + if(typeof callback == 'function') callback();
942 }
943
400 - NETDATA.getDomCharts = function(callback) {
401 - NETDATA.options.updated_dom = 0;
944 + // this is the main function - where everything starts
945 + NETDATA.init = function() {
946 + // this should be called only once
947
403 - NETDATA.options.targets = $('div[data-netdata]').filter(':visible')
404 - .bind('create', function(event, data) {
405 - var self = $(this);
948 + NETDATA.options.page_is_visible = 1;
949
407 - if(NETDATA.options.debug.chart_errors) {
408 - NETDATA.chartLibraries[self.data('chart-library')].create(this, data);
409 - self.data('chart-created', true);
410 - }
411 - else {
412 - try {
413 - NETDATA.chartLibraries[self.data('chart-library')].create(this, data);
414 - self.data('chart-created', true);
415 - }
416 - catch(err) {
417 - NETDATA.messageInABox(this, 'chart "' + self.data('netdata') + '" failed to be created as ' + self.data('chart-library'));
418 - self.data('chart-created', false);
419 - }
420 - }
421 - })
422 - .bind('update', function(event, data) {
423 - var self = $(this);
424 - if(NETDATA.options.debug.chart_errors)
425 - NETDATA.chartLibraries[self.data('chart-library')].update(this, data);
426 - else {
427 - try {
428 - NETDATA.chartLibraries[self.data('chart-library')].update(this, data);
429 - }
430 - catch(err) {
431 - NETDATA.messageInABox(this, 'chart "' + self.data('netdata') + '" failed to be updated as ' + self.data('chart-library'));
432 - self.data('chart-created', false);
433 - }
434 - }
435 - });
950 + $(window).blur(function() {
951 + NETDATA.options.page_is_visible = 0;
952 + if(NETDATA.options.debug.focus) console.log('Lost Focus!');
953 + });
954
437 - if(NETDATA.options.debug.main_loop)
438 - console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
955 + $(window).focus(function() {
956 + NETDATA.options.page_is_visible = 1;
957 + if(NETDATA.options.debug.focus) console.log('Focus restored!');
958 + });
959
440 - NETDATA.sizeDomCharts(NETDATA.options.targets, 0, function() {
441 - NETDATA.validateDomCharts(NETDATA.options.targets, 0, callback);
960 + if(typeof document.hasFocus == 'function' && !document.hasFocus()) {
961 + NETDATA.options.page_is_visible = 0;
962 + if(NETDATA.options.debug.focus) console.log('Document has no focus!');
963 + }
964 +
965 + NETDATA.getDomCharts(function() {
966 + NETDATA.chartRefresher(0);
967 });
968 }
969
@@ -454,153 +979,6 @@
979 //var c = new chart();
980 //c.color();
981
457 - NETDATA.chartValuesDownloader = function(element, callback, panning) {
458 - var self = $(element);
459 - var last = self.data('updated') || 0;
460 - var every = self.data('update-every') || 1;
461 -
462 - // check if this chart has to be refreshed now
463 - var now = new Date().getTime();
464 - if(!panning && last + every > now) {
465 - if(NETDATA.options.debug.chart_timing) console.log(self.data('netdata') + ' too soon - skipping.');
466 - if(typeof callback == 'function') callback();
467 - }
468 - else if(!panning && !self.visible(true)) {
469 - if(NETDATA.options.debug.visibility) console.log(self.data('netdata') + ' is NOT visible.');
470 - if(typeof callback == 'function') callback();
471 - }
472 - else {
473 - if(NETDATA.options.debug.visibility) console.log(self.data('netdata') + ' is visible, downloading data...');
474 - $.ajax( {
475 - url: NETDATA.generateChartDataURL.call(element), // self.data('chart-url'),
476 - crossDomain: true
477 - })
478 - .then(function(data) {
479 - var started = new Date().getTime();
480 -
481 - // if the result is JSON, find the latest update-every
482 - if(NETDATA.chartLibraries[self.data('chart-library')].jsonWrapper) {
483 - if(!panning && typeof data.update_every != 'undefined')
484 - self.data('update-every', data.update_every * 1000);
485 -
486 - if(typeof data.after != 'undefined')
487 - self.data('panning-after', data.after);
488 -
489 - if(typeof data.before != 'undefined')
490 - self.data('panning-before', data.before);
491 - }
492 -
493 - if(self.data('chart-created')) {
494 - if(NETDATA.options.debug.chart_calls) console.log('updating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
495 - self.trigger('update', [data]);
496 - // NETDATA.chartLibraries[self.data('chart-library')].update(element, data);
497 - }
498 - else {
499 - if(NETDATA.options.debug.chart_calls) console.log('creating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
500 - self.trigger('create', [data]);
501 - //NETDATA.chartLibraries[self.data('chart-library')].create(element, data);
502 - }
503 -
504 - var ended = new Date().getTime();
505 - self.data('updated', ended);
506 -
507 - var dt = ended - started;
508 -
509 - self.data('refresh-dt', dt);
510 - var element_name = self.data('dt-element-name') || null;
511 - if(element_name) {
512 - var element = document.getElementById(element_name) || null;
513 - if(element) {
514 - element.innerHTML = dt.toString();
515 - }
516 - }
517 - })
518 - .fail(function() {
519 - NETDATA.messageInABox(element, 'cannot download chart "' + self.data('netdata') + '" values from url "' + self.data('chart-url') + '"');
520 - })
521 - .always(function() {
522 - if(typeof callback == 'function') callback();
523 - });
524 - }
525 - };
526 -
527 - NETDATA.chartRefresher = function(index) {
528 - // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
529 -
530 - if(!NETDATA.options.page_is_visible) {
531 - if(NETDATA.options.debug.main_loop) console.log('waiting focus...');
532 - setTimeout(function() {
533 - NETDATA.chartRefresher(index);
534 - }, NETDATA.options.current.idle_lost_focus);
535 - }
536 - else {
537 - now = new Date().getTime();
538 -
539 - if(now < NETDATA.options.refreshed_stop_until) {
540 - if(NETDATA.options.debug.main_loop) console.log('refreshed stopped until...');
541 - setTimeout(function() {
542 - NETDATA.chartRefresher(index);
543 - }, NETDATA.options.current.idle_between_loops);
544 - }
545 - else if(NETDATA.options.updated_dom) {
546 - // the dom has been updated
547 - // get the dom parts again
548 - NETDATA.getDomCharts(function() {
549 - NETDATA.chartRefresher(0);
550 - });
551 - }
552 - else {
553 - var target = NETDATA.options.targets.get(index);
554 - if(target == null) {
555 - if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
556 - NETDATA.options.last_paused = now;
557 -
558 - setTimeout(function() {
559 - NETDATA.chartRefresher(0);
560 - }, NETDATA.options.current.idle_between_loops);
561 - }
562 - else {
563 - var self = $(target);
564 - if(!self.data('refresher-enabled')) {
565 - if(NETDATA.options.debug.main_loop) console.log('chart has refresher disabled...');
566 - NETDATA.chartRefresher(++index);
567 - }
568 - else if(self.data('chart-panning')) {
569 - if(NETDATA.options.debug.main_loop) console.log('chart is panning...');
570 - NETDATA.chartRefresher(++index);
571 - }
572 - else if(self.data('chart-zooming')) {
573 - if(NETDATA.options.debug.main_loop) console.log('chart is zooming...');
574 - NETDATA.chartRefresher(++index);
575 - }
576 - else if(self.data('chart-paused')) {
577 - if(NETDATA.options.debug.main_loop) console.log('chart is paused...');
578 - NETDATA.chartRefresher(++index);
579 - }
580 - else {
581 - if(now - NETDATA.options.last_paused < NETDATA.options.current.fast_render_timeframe) {
582 - if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
583 -
584 - NETDATA.chartValuesDownloader(target, function() {
585 - NETDATA.chartRefresher(++index);
586 - }, false);
587 - }
588 - else {
589 - if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
590 - NETDATA.options.last_paused = now;
591 -
592 - setTimeout(function() {
593 - NETDATA.chartValuesDownloader(target, function() {
594 - NETDATA.chartRefresher(++index);
595 - }, false);
596 - }, NETDATA.options.current.idle_between_charts);
597 - }
598 - }
599 - }
600 - }
601 - }
602 - }
603 -
982 // ----------------------------------------------------------------------------------------------------------------
983 // peity
984
@@ -626,30 +1004,20 @@
1004 };
1005
1006 NETDATA.peityChartUpdate = function(element, data) {
629 - var self = $(element);
630 - var instance = self.data('peity-instance');
631 - var ins = $(instance);
632 - ins.html(data.result);
633 -
1007 + var peity = $(data.state.peity_element);
1008 + peity.html(data.result);
1009 // peity.change() does not accept options
1010 // to pass width and height
636 - //ins.change();
637 - ins.peity('line', { width: self.data('calculated-width'), height: self.data('calculated-height') })
1011 + //peity.change();
1012 + peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1013 }
1014
1015 NETDATA.peityChartCreate = function(element, data) {
641 - var self = $(element);
642 -
643 - var uuid = NETDATA.guid();
644 - element.innerHTML = '<div id="' + uuid + '">' + data.result + '</div>';
645 - var instance = document.getElementById(uuid);
646 - var ins = $(instance);
647 -
648 - ins.peity('line', { width: self.data('calculated-width'), height: self.data('calculated-height') })
1016 + element.innerHTML = '<div id="peity-' + data.state.uuid + '">' + data.result + '</div>';
1017 + data.state.peity_element = document.getElementById('peity-' + data.state.uuid);
1018 + var peity = $(data.state.peity_element);
1019
650 - self.data('peity-uuid', uuid)
651 - .data('peity-instance', instance)
652 - .data('chart-created', true);
1020 + peity.peity('line', { width: data.state.calculated_width, height: data.state.calculated_height });
1021 }
1022
1023 // ----------------------------------------------------------------------------------------------------------------
@@ -677,19 +1045,18 @@
1045 };
1046
1047 NETDATA.sparklineChartUpdate = function(element, data) {
680 - var self = $(element);
681 - var options = self.data('sparkline-options');
682 - options.width = self.data('calculated-width');
683 - options.height = self.data('calculated-height');
684 - self.sparkline(data.result, options);
1048 + data.state.sparkline_options.width = data.state.calculated_width;
1049 + data.state.sparkline_options.height = data.state.calculated_height;
1050 +
1051 + spark = $(data.state.sparkline_element);
1052 + spark.sparkline(data.result, data.state.sparkline_options);
1053 }
1054
1055 NETDATA.sparklineChartCreate = function(element, data) {
1056 var self = $(element);
689 - var chart = self.data('chart');
1057 var type = self.data('sparkline-type') || 'line';
1058 var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
692 - var fillColor = self.data('sparkline-fillcolor') || (chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
1059 + var fillColor = self.data('sparkline-fillcolor') || (data.state.chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
1060 var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
1061 var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
1062 var composite = self.data('sparkline-composite') || undefined;
@@ -723,7 +1090,7 @@
1090 var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
1091 var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
1092 var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
726 - var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + chart.units;
1093 + var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + data.state.chart.units;
1094 var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
1095 var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
1096 var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
@@ -734,7 +1101,7 @@
1101 var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
1102 var animatedZooms = self.data('sparkline-animatedzooms') || false;
1103
737 - var options = {
1104 + data.state.sparkline_options = {
1105 type: type,
1106 lineColor: lineColor,
1107 fillColor: fillColor,
@@ -768,7 +1135,7 @@
1135 highlightColor: highlightColor,
1136 tooltipContainer: tooltipContainer,
1137 tooltipClassname: tooltipClassname,
771 - tooltipChartTitle: chart.title,
1138 + tooltipChartTitle: data.state.chart.title,
1139 tooltipFormat: tooltipFormat,
1140 tooltipPrefix: tooltipPrefix,
1141 tooltipSuffix: tooltipSuffix,
@@ -781,43 +1148,92 @@
1148 numberDecimalMark: numberDecimalMark,
1149 numberDigitGroupCount: numberDigitGroupCount,
1150 animatedZooms: animatedZooms,
784 - width: self.data('calculated-width'),
785 - height: self.data('calculated-height')
1151 + width: data.state.calculated_width,
1152 + height: data.state.calculated_height
1153 };
1154
788 - var uuid = NETDATA.guid();
789 - element.innerHTML = '<div style="display: inline-block; position: relative;" id="' + uuid + '"></div>';
790 - var div = document.getElementById(uuid);
1155 + element.innerHTML = '<div id="sparkline-' + data.state.uuid + '" style="display: inline-block; position: relative;"></div>';
1156 + data.state.sparkline_element = document.getElementById('sparkline-' + data.state.uuid);
1157
792 - self.sparkline(data.result, options);
793 - self.data('sparkline-options', options)
794 - .data('uuid', uuid)
795 - .data('chart-created', true);
1158 + spark = $(data.state.sparkline_element);
1159 + spark.sparkline(data.result, data.state.sparkline_options);
1160 };
1161
1162 // ----------------------------------------------------------------------------------------------------------------
1163 // dygraph
1164
801 - NETDATA.dygraphAllCharts = [];
802 - NETDATA.dygraphInitSync = function(callback) {
803 - //$.getScript(NETDATA.serverDefault + 'dygraph-synchronizer.js')
804 - // .always(function() {
805 - if(typeof callback == "function")
806 - callback();
807 - // })
1165 + NETDATA.dygraph = {
1166 + sync: false,
1167 + paused: []
1168 + };
1169 +
1170 + NETDATA.dygraph.syncStart = function(state, event, x, points, row, seriesName) {
1171 + if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStart()');
1172 + state.pauseChart();
1173 +
1174 + var dygraph = state.dygraph_instance;
1175 +
1176 + if(!NETDATA.dygraph.sync) {
1177 + $.each(NETDATA.options.targets, function(i, target) {
1178 + var st = NETDATA.chartState(target);
1179 + if(typeof st.dygraph_instance == 'object' && st.library_name == state.library_name && st.canBeAutoRefreshed(false)) {
1180 + NETDATA.dygraph.paused.push(st);
1181 + }
1182 + });
1183 + }
1184 +
1185 + $.each(NETDATA.dygraph.paused, function(i, st) {
1186 + st.setSelection(x);
1187 + });
1188 +
1189 }
1190
810 - NETDATA.dygraphSync = null;
811 - NETDATA.dygraphSyncAll = function() {
812 - if(NETDATA.dygraphSync) {
813 - NETDATA.dygraphSync.detach();
814 - NETDATA.dygraphSync = null;
1191 + NETDATA.dygraph.syncStop = function(state) {
1192 + if(NETDATA.options.debug.dygraph || state.debug) console.log('dygraph.syncStop()');
1193 +
1194 + if(!NETDATA.dygraph.sync) {
1195 + $.each(NETDATA.dygraph.paused, function(i, st) {
1196 + st.clearSelection();
1197 + });
1198 +
1199 + NETDATA.dygraph.sync = false;
1200 + NETDATA.dygraph.paused = [];
1201 }
1202
817 - NETDATA.dygraphSync = Dygraph.synchronize(NETDATA.dygraphAllCharts, {
818 - selection: true,
819 - zoom: false
820 - });
1203 + state.unpauseChart();
1204 + }
1205 +
1206 + NETDATA.dygraph.resetChart = function(element, dygraph) {
1207 + if(NETDATA.options.debug.dygraph) console.log('dygraph.resetChart()');
1208 +
1209 + state = NETDATA.chartState(element);
1210 + state.resetChart();
1211 + if(NETDATA.globalPanAndZoom.clearMaster());
1212 + }
1213 +
1214 + NETDATA.dygraph.zoomOrPan = function(element, dygraph, after, before) {
1215 + if(NETDATA.options.debug.dygraph) console.log('>>>> dygraph.zoomOrPan(element, dygraph, after:' + after + ', before: ' + before + ')');
1216 +
1217 + state = NETDATA.chartState(element);
1218 + state.updateChartPanOrZoom(after, before);
1219 + return;
1220 + }
1221 +
1222 + NETDATA.dygraphSetSelection = function(state, t) {
1223 + var r = state.calculateRowForTime(t);
1224 + if(r != -1) {
1225 + state.dygraph_instance.setSelection(r);
1226 + state.pauseChart();
1227 + }
1228 + else {
1229 + state.dygraph_instance.clearSelection();
1230 + state.unpauseChart();
1231 + }
1232 + }
1233 +
1234 + NETDATA.dygraphclearSelection = function(state, t) {
1235 + state.dygraph_instance.clearSelection();
1236 + state.unpauseChart();
1237 }
1238
1239 NETDATA.dygraphInitialize = function(callback) {
@@ -830,7 +1246,8 @@
1246 NETDATA.error(100, NETDATA.dygraph_js);
1247 })
1248 .always(function() {
833 - NETDATA.dygraphInitSync(callback);
1249 + if(typeof callback == "function")
1250 + callback();
1251 })
1252 }
1253 else {
@@ -840,90 +1257,25 @@
1257 }
1258 };
1259
843 - NETDATA.dygraphReset = function(element, dygraph) {
844 - if(NETDATA.options.debug.dygraph) console.log('dygraphReset()');
845 -
846 - self = $(element);
847 -
848 - if(self.data('chart-panning') || self.data('chart-paused')) {
849 - self.data('chart-panning', false)
850 - .data('chart-zooming', false)
851 - .data('chart-paused', false)
852 - .data('panning-before', 0)
853 - .data('panning-after', 0)
854 - .data('updated', 0)
855 - .data('panning-before-max', 0);
856 -
857 - NETDATA.options.refreshed_stop_until = 0;
858 - NETDATA.chartValuesDownloader(element, null, false);
859 - }
860 - }
861 -
862 - NETDATA.dygraphZoomOrPan = function(element, dygraph, after, before, zoom) {
863 - if(NETDATA.options.debug.dygraph) console.log('dygraphZoomOrPan()');
864 -
865 - self = $(element);
866 -
867 - var before_old = self.data('panning-before');
868 - var after_old = self.data('panning-after');
869 - var range_old = before_old - after_old;
870 -
871 - var shift = Math.abs(before_old - before);
872 -
873 - var range = before - after;
874 -
875 - if((shift > range_old / 20 || zoom) && range > 10) {
876 - // the user is panning
877 - if(NETDATA.options.debug.dygraph) console.log('dygraphZoomOrPan() ' + after_old + ' - ' + before_old + ', NEW: ' + after + ' - ' + before + ', shift by: ' + shift + ' old range: ' + (before_old - after_old).toString() + ' new range: ' + (before - after).toString());
878 -
879 - if(!self.data('chart-panning') && !self.data('chart-zooming')) {
880 - self.data('chart-panning', true)
881 - .data('panning-before-max', before);
882 - }
883 -
884 - var before_max = self.data('panning-before-max');
885 - if(zoom || before <= before_max) {
886 - NETDATA.options.refreshed_stop_until = now + 2000;
887 -
888 - if(NETDATA.options.debug.dygraph) console.log('dygraphZoomOrPan() rendering.');
889 -
890 - if(zoom) {
891 - self.data('chart-panning', false)
892 - .data('chart-zooming', true);
893 - }
894 - else {
895 - self.data('chart-panning', true)
896 - .data('chart-zooming', false);
897 - }
898 -
899 - self.data('chart-paused', true)
900 - .data('panning-before', before)
901 - .data('panning-after', after)
902 - .data('updated', 0);
903 -
904 - NETDATA.chartValuesDownloader(element, null, true);
905 - }
906 - }
907 - }
908 -
1260 NETDATA.dygraphChartUpdate = function(element, data) {
910 - if(NETDATA.options.debug.dygraph) console.log('dygraphChartUpdate()');
1261 + if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate()');
1262
912 - var self = $(element);
913 - var dygraph = self.data('dygraph-instance');
1263 + var dygraph = data.state.dygraph_instance;
1264
915 - if(self.data('chart-panning')) {
1265 + if(data.state.mode.name == 'pan') {
1266 + if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() loose update');
1267 dygraph.updateOptions({
1268 file: data.result.data,
1269 labels: data.result.labels,
919 - labelsDivWidth: self.width() - 70
1270 + labelsDivWidth: data.state.calculated_width - 70
1271 });
1272 }
1273 else {
1274 + if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartUpdate() strict update');
1275 dygraph.updateOptions({
1276 file: data.result.data,
1277 labels: data.result.labels,
926 - labelsDivWidth: self.width() - 70,
1278 + labelsDivWidth: data.state.calculated_width - 70,
1279 dateWindow: null,
1280 valueRange: null
1281 });
@@ -931,19 +1283,18 @@
1283 };
1284
1285 NETDATA.dygraphChartCreate = function(element, data) {
934 - if(NETDATA.options.debug.dygraph) console.log('dygraphChartCreate()');
1286 + if(NETDATA.options.debug.dygraph || data.state.debug) console.log('dygraphChartCreate()');
1287
1288 var self = $(element);
937 - var chart = self.data('chart');
938 - var title = self.data('dygraph-title') || chart.title;
1289 + var title = self.data('dygraph-title') || data.state.chart.title;
1290 var titleHeight = self.data('dygraph-titleheight') || 20;
1291 var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
1292 var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
1293 var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
943 - var stackedGraph = self.data('dygraph-stackedgraph') || (chart.chart_type == 'stacked')?true:false;
1294 + var stackedGraph = self.data('dygraph-stackedgraph') || (data.state.chart.chart_type == 'stacked')?true:false;
1295 var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
1296 var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
946 - var fillGraph = self.data('dygraph-fillgraph') || (chart.chart_type == 'area')?true:false;
1297 + var fillGraph = self.data('dygraph-fillgraph') || (data.state.chart.chart_type == 'area')?true:false;
1298 var drawPoints = self.data('dygraph-drawpoints') || false;
1299 var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
1300 var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
@@ -965,11 +1316,11 @@
1316 var pointSize = self.data('dygraph-pointsize') || 1;
1317 var stepPlot = self.data('dygraph-stepplot') || false;
1318 var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
968 - var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (chart.chart_type == 'stacked')?1.0:0.0;
1319 + var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (data.state.chart.chart_type == 'stacked')?1.0:0.0;
1320 var strokePattern = self.data('dygraph-strokepattern') || undefined;
1321 var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
1322 var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || { strokeWidth: 1.5 };
972 - var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (chart.chart_type == 'stacked')?0.7:0.5;
1323 + var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (data.state.chart.chart_type == 'stacked')?0.7:0.5;
1324 var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
1325 var showRangeSelector = self.data('dygraph-showrangeselector') || false;
1326 var showRoller = self.data('dygraph-showroller') || false;
@@ -981,10 +1332,10 @@
1332 var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
1333 var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
1334
984 - var options = {
1335 + data.state.dygraph_options = {
1336 title: title,
1337 titleHeight: titleHeight,
987 - ylabel: chart.units,
1338 + ylabel: data.state.chart.units,
1339 yLabelWidth: yLabelWidth,
1340 connectSeparatedPoints: connectSeparatedPoints,
1341 drawPoints: drawPoints,
@@ -1048,47 +1399,57 @@
1399 }
1400 },
1401 drawCallback: function(dygraph, is_initial) {
1051 - if(NETDATA.options.debug.dygraph) console.log('dygraphDrawCallback()');
1402 + if(data.state.mode.name != 'auto') {
1403 + if(NETDATA.options.debug.dygraph) console.log('dygraphDrawCallback()');
1404
1053 - var x_range = dygraph.xAxisRange();
1054 - var after = Math.round(x_range[0] / 1000);
1055 - var before = Math.round(x_range[1] / 1000);
1405 + var x_range = dygraph.xAxisRange();
1406 + var after = Math.round(x_range[0]);
1407 + var before = Math.round(x_range[1]);
1408
1057 - NETDATA.dygraphZoomOrPan(element, this, after, before, false, true);
1409 + NETDATA.dygraph.zoomOrPan(element, this, after, before);
1410 + }
1411 },
1412 zoomCallback: function(minDate, maxDate, yRanges) {
1413 if(NETDATA.options.debug.dygraph) console.log('dygraphZoomCallback()');
1061 - NETDATA.dygraphZoomOrPan(element, this, Math.round(minDate / 1000), Math.round(maxDate / 1000), false, true);
1414 + NETDATA.dygraph.zoomOrPan(element, this, minDate, maxDate);
1415 },
1416 highlightCallback: function(event, x, points, row, seriesName) {
1417 if(NETDATA.options.debug.dygraph) console.log('dygraphHighlightCallback()');
1065 - self.data('chart-paused', true);
1418 + NETDATA.dygraph.syncStart(data.state, event, x, points, row, seriesName);
1419 },
1420 unhighlightCallback: function(event) {
1421 if(NETDATA.options.debug.dygraph) console.log('dygraphUnhighlightCallback()');
1069 - self.data('chart-paused', false);
1422 + NETDATA.dygraph.syncStop(data.state);
1423 },
1424 interactionModel : {
1425 mousedown: function(event, dygraph, context) {
1426 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDown()');
1427
1075 - // Right-click should not initiate a zoom.
1428 + // Right-click should not initiate a zoom.
1429 if (event.button && event.button == 2) return;
1430
1431 context.initializeMouseDown(event, dygraph, context);
1432
1080 - if (event.altKey || event.shiftKey)
1433 + if (event.altKey || event.shiftKey) {
1434 + data.state.setMode('zoom');
1435 Dygraph.startZoom(event, dygraph, context);
1082 - else
1436 + }
1437 + else {
1438 + data.state.setMode('pan');
1439 Dygraph.startPan(event, dygraph, context);
1440 + }
1441 },
1442 mousemove: function(event, dygraph, context) {
1443 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseMove()');
1444
1088 - if (context.isPanning)
1445 + if (context.isPanning) {
1446 + data.state.setMode('pan');
1447 Dygraph.movePan(event, dygraph, context);
1090 - else if (context.isZooming)
1448 + }
1449 + else if (context.isZooming) {
1450 + data.state.setMode('zoom');
1451 Dygraph.moveZoom(event, dygraph, context);
1452 + }
1453 },
1454 mouseup: function(event, dygraph, context) {
1455 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseUp()');
@@ -1104,7 +1465,7 @@
1465 },
1466 dblclick: function(event, dygraph, context) {
1467 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseDoubleClick()');
1107 - NETDATA.dygraphReset(element, dygraph);
1468 + NETDATA.dygraph.resetChart(element, dygraph);
1469 },
1470 mousewheel: function(event, dygraph, context) {
1471 if(NETDATA.options.debug.dygraph) console.log('dygraphMouseWheel()');
@@ -1112,27 +1473,31 @@
1473 if(event.altKey || event.shiftKey) {
1474 // http://dygraphs.com/gallery/interaction-api.js
1475 var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
1115 - var percentage = normal / 10;
1476 + var percentage = normal / 25;
1477
1117 - var before_old = self.data('panning-before');
1118 - var after_old = self.data('panning-after');
1478 + var before_old = data.state.mode.before_ms;
1479 + var after_old = data.state.mode.after_ms;
1480 var range_old = before_old - after_old;
1481
1482 var range = range_old * ( 1 - percentage );
1483 var dt = Math.round((range_old - range) / 2);
1123 - before = before_old - dt;
1124 - after = after_old + dt;
1484 +
1485 + var before = before_old - dt;
1486 + var after = after_old + dt;
1487
1488 if(NETDATA.options.debug.dygraph) console.log('percent: ' + percentage + ' from ' + after_old + ' - ' + before_old + ' to ' + after + ' - ' + before + ', range from ' + (before_old - after_old).toString() + ' to ' + (before - after).toString());
1489
1128 - NETDATA.dygraphZoomOrPan(element, dygraph, after, before, true);
1490 + data.state.setMode('zoom');
1491 + NETDATA.dygraph.zoomOrPan(element, dygraph, after, before);
1492 }
1493 },
1494 touchstart: function(event, dygraph, context) {
1495 Dygraph.Interaction.startTouch(event, dygraph, context);
1133 - //context.touchDirections.y = 0;
1496 + context.touchDirections = { x: true, y: false };
1497 + data.state.setMode('zoom');
1498 },
1499 touchmove: function(event, dygraph, context) {
1500 + //Dygraph.cancelEvent(event);
1501 Dygraph.Interaction.moveTouch(event, dygraph, context);
1502 },
1503 touchend: function(event, dygraph, context) {
@@ -1141,20 +1506,10 @@
1506 }
1507 };
1508
1144 - var uuid = NETDATA.guid();
1145 - self.html('<div id="' + uuid + '" style="width: 100%; height: 100%;"></div>');
1146 -
1147 - var dchart = new Dygraph(document.getElementById(uuid),
1148 - data.result.data, options);
1509 + self.html('<div id="dygraph-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>');
1510
1150 - self.data('dygraph-instance', dchart)
1151 - .data('dygraph-options', options)
1152 - .data('uuid', uuid)
1153 - .data('chart-created', true);
1154 -
1155 - //NETDATA.dygraphAllCharts.push(dchart);
1156 - //if(NETDATA.dygraphAllCharts.length > 1)
1157 - // NETDATA.dygraphSyncAll();
1511 + data.state.dygraph_instance = new Dygraph(document.getElementById('dygraph-' + data.state.uuid),
1512 + data.result.data, data.state.dygraph_options);
1513 };
1514
1515 // ----------------------------------------------------------------------------------------------------------------
@@ -1176,26 +1531,27 @@
1531 callback();
1532 }
1533 }
1534 + else {
1535 + var fileref = document.createElement("link");
1536 + fileref.setAttribute("rel", "stylesheet");
1537 + fileref.setAttribute("type", "text/css");
1538 + fileref.setAttribute("href", NETDATA.morris_css);
1539
1180 - var fileref = document.createElement("link");
1181 - fileref.setAttribute("rel", "stylesheet");
1182 - fileref.setAttribute("type", "text/css");
1183 - fileref.setAttribute("href", NETDATA.morris_css);
1184 -
1185 - if (typeof fileref != "undefined")
1186 - document.getElementsByTagName("head")[0].appendChild(fileref);
1540 + if (typeof fileref != "undefined")
1541 + document.getElementsByTagName("head")[0].appendChild(fileref);
1542
1188 - $.getScript(NETDATA.morris_js)
1189 - .done(function() {
1190 - NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1191 - })
1192 - .fail(function() {
1193 - NETDATA.error(100, NETDATA.morris_js);
1194 - })
1195 - .always(function() {
1196 - if(typeof callback == "function")
1197 - callback();
1198 - })
1543 + $.getScript(NETDATA.morris_js)
1544 + .done(function() {
1545 + NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
1546 + })
1547 + .fail(function() {
1548 + NETDATA.error(100, NETDATA.morris_js);
1549 + })
1550 + .always(function() {
1551 + if(typeof callback == "function")
1552 + callback();
1553 + })
1554 + }
1555 }
1556 else {
1557 NETDATA.chartLibraries.morris.enabled = false;
@@ -1205,26 +1561,15 @@
1561 };
1562
1563 NETDATA.morrisChartUpdate = function(element, data) {
1208 - var self = $(element);
1209 - var morris = self.data('morris-instance');
1210 -
1211 - if(morris != null) {
1212 - console.log('updating morris');
1213 - morris.setData(data.result.data);
1214 - }
1215 - else
1216 - console.log('not updating morris');
1564 + data.state.morris_instance.setData(data.result.data);
1565 };
1566
1567 NETDATA.morrisChartCreate = function(element, data) {
1220 - var self = $(element);
1221 - var chart = self.data('chart');
1568
1223 - var uuid = NETDATA.guid();
1224 - self.html('<div id="' + uuid + '" style="width: ' + self.data('calculated-width') + 'px; height: ' + self.data('calculated-height') + 'px;"></div>');
1569 + element.innerHTML = '<div id="morris-' + data.state.uuid + '" style="width: ' + data.state.calculated_width + 'px; height: ' + data.state.calculated_height + 'px;"></div>';
1570
1571 var options = {
1227 - element: uuid,
1572 + element: 'morris-' + data.state.uuid,
1573 data: data.result.data,
1574 xkey: 'time',
1575 ykeys: data.dimension_names,
@@ -1239,18 +1584,18 @@
1584 };
1585
1586 var morris;
1242 - if(chart.chart_type == 'line')
1587 + if(data.state.chart.chart_type == 'line')
1588 morris = new Morris.Line(options);
1589
1245 - else if(chart.chart_type == 'area') {
1590 + else if(data.state.chart.chart_type == 'area') {
1591 options.behaveLikeLine = true;
1592 morris = new Morris.Area(options);
1593 }
1594 else // stacked
1595 morris = new Morris.Area(options);
1596
1252 - self.data('morris-instance', morris)
1253 - .data('chart-created', true);
1597 + data.state.morris_instance = morris;
1598 + data.state.morris_options = options;
1599 };
1600
1601 // ----------------------------------------------------------------------------------------------------------------
@@ -1281,8 +1626,8 @@
1626 var self = $(element);
1627
1628 self.raphael(data, {
1284 - width: self.data('calculated-width'),
1285 - height: self.data('calculated-height')
1629 + width: data.state.calculated_width,
1630 + height: data.state.calculated_height
1631 })
1632 };
1633
@@ -1290,10 +1635,9 @@
1635 var self = $(element);
1636
1637 self.raphael(data, {
1293 - width: self.data('calculated-width'),
1294 - height: self.data('calculated-height')
1638 + width: data.state.calculated_width,
1639 + height: data.state.calculated_height
1640 })
1296 - .data('chart-created', true);
1641 };
1642
1643 // ----------------------------------------------------------------------------------------------------------------
@@ -1324,28 +1668,19 @@
1668 };
1669
1670 NETDATA.googleChartUpdate = function(element, data) {
1327 - var self = $(element);
1328 - var gchart = self.data('google-instance');
1329 - var options = self.data('google-options');
1330 -
1671 var datatable = new google.visualization.DataTable(data.result);
1332 -
1333 - gchart.draw(datatable, options);
1672 + data.state.google_instance.draw(datatable, data.state.google_options);
1673 };
1674
1675 NETDATA.googleChartCreate = function(element, data) {
1337 - var self = $(element);
1338 - var chart = self.data('chart');
1339 -
1676 var datatable = new google.visualization.DataTable(data.result);
1341 - var gchart;
1677
1678 var options = {
1679 // do not set width, height - the chart resizes itself
1345 - //width: self.data('calculated-width'),
1346 - //height: self.data('calculated-height'),
1680 + //width: data.state.calculated_width,
1681 + //height: data.state.calculated_height,
1682 lineWidth: 1,
1348 - title: chart.title,
1683 + title: data.state.chart.title,
1684 fontSize: 11,
1685 hAxis: {
1686 // title: "Time of Day",
@@ -1361,7 +1696,7 @@
1696 }
1697 },
1698 vAxis: {
1364 - title: chart.units,
1699 + title: data.state.chart.units,
1700 viewWindowMode: 'pretty',
1701 minValue: -0.1,
1702 maxValue: 0.1,
@@ -1400,13 +1735,13 @@
1735 isStacked: false
1736 };
1737
1403 - var uuid = NETDATA.guid();
1404 - self.html('<div id="' + uuid + '" style="width: 100%; height: 100%;"></div>');
1738 + element.innerHTML = '<div id="google-' + data.state.uuid + '" style="width: 100%; height: 100%;"></div>';
1739
1406 - switch(chart.chart_type) {
1740 + var gchart;
1741 + switch(data.state.chart.chart_type) {
1742 case "area":
1743 options.vAxis.viewWindowMode = 'maximized';
1409 - gchart = new google.visualization.AreaChart(document.getElementById(uuid));
1744 + gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1745 break;
1746
1747 case "stacked":
@@ -1415,22 +1750,20 @@
1750 options.vAxis.viewWindowMode = 'maximized';
1751 options.vAxis.minValue = null;
1752 options.vAxis.maxValue = null;
1418 - gchart = new google.visualization.AreaChart(document.getElementById(uuid));
1753 + gchart = new google.visualization.AreaChart(document.getElementById('google-' + data.state.uuid));
1754 break;
1755
1756 default:
1757 case "line":
1758 options.lineWidth = 2;
1424 - gchart = new google.visualization.LineChart(document.getElementById(uuid));
1759 + gchart = new google.visualization.LineChart(document.getElementById('google-' + data.state.uuid));
1760 break;
1761 }
1762
1763 gchart.draw(datatable, options);
1764
1430 - self.data('google-instance', gchart)
1431 - .data('google-options', options)
1432 - .data('uuid', uuid)
1433 - .data('chart-created', true);
1765 + data.state.google_instance = gchart;
1766 + data.state.google_options = options;
1767 };
1768
1769 // ----------------------------------------------------------------------------------------------------------------
@@ -1441,6 +1774,8 @@
1774 initialize: NETDATA.dygraphInitialize,
1775 create: NETDATA.dygraphChartCreate,
1776 update: NETDATA.dygraphChartUpdate,
1777 + setSelection: NETDATA.dygraphSetSelection,
1778 + clearSelection: NETDATA.dygraphClearSelection,
1779 initialized: false,
1780 enabled: true,
1781 format: 'json',
@@ -1453,6 +1788,8 @@
1788 initialize: NETDATA.sparklineInitialize,
1789 create: NETDATA.sparklineChartCreate,
1790 update: NETDATA.sparklineChartUpdate,
1791 + setSelection: null,
1792 + clearSelection: null,
1793 initialized: false,
1794 enabled: true,
1795 format: 'array',
@@ -1465,6 +1802,8 @@
1802 initialize: NETDATA.peityInitialize,
1803 create: NETDATA.peityChartCreate,
1804 update: NETDATA.peityChartUpdate,
1805 + setSelection: null,
1806 + clearSelection: null,
1807 initialized: false,
1808 enabled: true,
1809 format: 'ssvcomma',
@@ -1477,6 +1816,8 @@
1816 initialize: NETDATA.morrisInitialize,
1817 create: NETDATA.morrisChartCreate,
1818 update: NETDATA.morrisChartUpdate,
1819 + setSelection: null,
1820 + clearSelection: null,
1821 initialized: false,
1822 enabled: true,
1823 format: 'json',
@@ -1489,6 +1830,8 @@
1830 initialize: NETDATA.googleInitialize,
1831 create: NETDATA.googleChartCreate,
1832 update: NETDATA.googleChartUpdate,
1833 + setSelection: null,
1834 + clearSelection: null,
1835 initialized: false,
1836 enabled: true,
1837 format: 'datatable',
@@ -1501,11 +1844,13 @@
1844 initialize: NETDATA.raphaelInitialize,
1845 create: NETDATA.raphaelChartCreate,
1846 update: NETDATA.raphaelChartUpdate,
1847 + setSelection: null,
1848 + clearSelection: null,
1849 initialized: false,
1850 enabled: true,
1851 format: 'json',
1852 options: '',
1508 - jsonWrapper: false,
1853 + jsonWrapper: true,
1854 pixels_per_point: 1,
1855 detects_dimensions_on_update: false
1856 }
@@ -1518,7 +1863,7 @@
1863 NETDATA.chartLibraries[library].initialized = true;
1864 NETDATA.chartLibraries[library].enabled = true;
1865
1521 - console.log(NETDATA.chartLibraries);
1866 + // console.log(NETDATA.chartLibraries);
1867 }
1868
1869 // ----------------------------------------------------------------------------------------------------------------