@cryptotaxi247 / netdata-1 / commits / f4456c3d4

fixed minor issues throughout the code (mainly types); dashboard has now a watermark while loading to show it downloads or renders something; a lot of code cleanup in javascript

Costa Tsaousis (ktsaou) committed Dec 29, 2015 at 04:21 UTC f4456c3d41da3d23520106fdb8b5d9f66267a3cb
32 files changed +1553 -1580
src/appconfig.c
+1 -1
@@ -202,7 +202,7 @@ int load_config(char *filename, int overwrite_used)
202 continue;
203 }
204
205 - int len = strlen(s);
205 + int len = (int) strlen(s);
206 if(*s == '[' && s[len - 1] == ']') {
207 // new section
208 s[len - 1] = '\0';
src/apps_plugin.c
+25 -26
@@ -45,8 +45,6 @@ int debug = 0;
45 int update_every = 1;
46 unsigned long long file_counter = 0;
47
48 -#define PROC_BUFFER 4096
49 -
48 // ----------------------------------------------------------------------------
49 // memory debugger
50
@@ -64,14 +62,14 @@ struct allocations {
62 void *mark_allocation(void *allocated_ptr, size_t size_without_overheads) {
63 uint32_t *real_ptr = (uint32_t *)allocated_ptr;
64 real_ptr[0] = MALLOC_MARK;
67 - real_ptr[1] = size_without_overheads;
65 + real_ptr[1] = (uint32_t) size_without_overheads;
66
67 uint32_t *end_ptr = (uint32_t *)(allocated_ptr + MALLOC_PREFIX + size_without_overheads);
68 end_ptr[0] = MALLOC_MARK;
69
70 // fprintf(stderr, "MEMORY_POINTER: Allocated at %p, returning %p.\n", allocated_ptr, (void *)(allocated_ptr + MALLOC_PREFIX));
71
74 - return (void *)(allocated_ptr + MALLOC_PREFIX);
72 + return allocated_ptr + MALLOC_PREFIX;
73 }
74
75 void *check_allocation(const char *file, int line, const char *function, void *marked_ptr, size_t *size_without_overheads_ptr) {
@@ -249,23 +247,23 @@ long get_pid_max(void) {
247
248 unsigned long long get_hertz(void)
249 {
252 - unsigned long long hz = 1;
250 + unsigned long long myhz = 1;
251
252 #ifdef _SC_CLK_TCK
255 - if((hz = sysconf(_SC_CLK_TCK)) > 0) {
256 - return hz;
253 + if((myhz = (unsigned long long int) sysconf(_SC_CLK_TCK)) > 0) {
254 + return myhz;
255 }
256 #endif
257
258 #ifdef HZ
261 - hz = (unsigned long long)HZ; /* <asm/param.h> */
259 + myhz = HZ; /* <asm/param.h> */
260 #else
261 /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
262 hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
263 #endif
264
267 - error("Unknown HZ value. Assuming %llu.", hz);
268 - return hz;
265 + error("Unknown HZ value. Assuming %llu.", myhz);
266 + return myhz;
267 }
268
269
@@ -650,7 +648,7 @@ int read_proc_pid_stat(struct pid_stat *p) {
648 // p->pid = atol(procfile_lineword(ff, 0, 0+i));
649 // comm is at 1
650 // p->state = *(procfile_lineword(ff, 0, 2+i));
653 - p->ppid = atol(procfile_lineword(ff, 0, 3+i));
651 + p->ppid = (int32_t) atol(procfile_lineword(ff, 0, 3 + i));
652 // p->pgrp = atol(procfile_lineword(ff, 0, 4+i));
653 // p->session = atol(procfile_lineword(ff, 0, 5+i));
654 // p->tty_nr = atol(procfile_lineword(ff, 0, 6+i));
@@ -666,7 +664,7 @@ int read_proc_pid_stat(struct pid_stat *p) {
664 p->cstime = strtoull(procfile_lineword(ff, 0, 16+i), NULL, 10);
665 // p->priority = strtoull(procfile_lineword(ff, 0, 17+i), NULL, 10);
666 // p->nice = strtoull(procfile_lineword(ff, 0, 18+i), NULL, 10);
669 - p->num_threads = atol(procfile_lineword(ff, 0, 19+i));
667 + p->num_threads = (int32_t) atol(procfile_lineword(ff, 0, 19 + i));
668 // p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20+i), NULL, 10);
669 // p->starttime = strtoull(procfile_lineword(ff, 0, 21+i), NULL, 10);
670 // p->vsize = strtoull(procfile_lineword(ff, 0, 22+i), NULL, 10);
@@ -809,8 +807,8 @@ struct file_descriptor {
807 uint32_t hash;
808 const char *name;
809 int type;
812 - long count;
813 - long pos;
810 + int count;
811 + int pos;
812 } *all_files = NULL;
813
814 int all_files_len = 0;
@@ -871,7 +869,7 @@ void file_descriptor_not_used(int id)
869 return;
870 }
871
874 - if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %ld).\n", id, all_files[id].count);
872 + if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
873
874 if(all_files[id].count > 0) {
875 all_files[id].count--;
@@ -889,7 +887,7 @@ void file_descriptor_not_used(int id)
887 else error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
888 }
889
892 -unsigned long file_descriptor_find_or_add(const char *name)
890 +int file_descriptor_find_or_add(const char *name)
891 {
892 static int last_pos = 0;
893 uint32_t hash = simple_hash(name);
@@ -899,7 +897,7 @@ unsigned long file_descriptor_find_or_add(const char *name)
897 struct file_descriptor *fd = file_descriptor_find(name, hash);
898 if(fd) {
899 // found
902 - if(debug) fprintf(stderr, "apps.plugin: >> found on slot %ld\n", fd->pos);
900 + if(debug) fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos);
901 fd->count++;
902 return fd->pos;
903 }
@@ -968,7 +966,7 @@ unsigned long file_descriptor_find_or_add(const char *name)
966
967 // else we have an empty slot in 'c'
968
971 - int type = FILETYPE_OTHER;
969 + int type;
970 if(name[0] == '/') type = FILETYPE_FILE;
971 else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
972 else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
@@ -1041,7 +1039,7 @@ int update_from_proc(void)
1039
1040 while((file = readdir(dir))) {
1041 char *endptr = file->d_name;
1044 - pid_t pid = strtoul(file->d_name, &endptr, 10);
1042 + pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
1043 if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
1044
1045 p = get_pid_entry(pid);
@@ -1119,7 +1117,7 @@ int update_from_proc(void)
1117 if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
1118
1119 // check if the fds array is small
1122 - int fdid = atol(de->d_name);
1120 + int fdid = atoi(de->d_name);
1121 if(fdid < 0) continue;
1122 if(fdid >= p->fds_size) {
1123 // it is small, extend it
@@ -1139,7 +1137,7 @@ int update_from_proc(void)
1137 // we don't know this fd, get it
1138
1139 sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name);
1142 - int l = readlink(fdname, linkname, FILENAME_MAX);
1140 + ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1141 if(l == -1) {
1142 if(debug || (p->target && p->target->debug)) {
1143 if(!count_errors++ || debug || (p->target && p->target->debug))
@@ -1337,7 +1335,7 @@ void update_statistics(void)
1335 for (w = target_root; w ; w = w->next) {
1336 targets++;
1337
1340 - w->fds = calloc(sizeof(int), all_files_size);
1338 + w->fds = calloc(sizeof(int), (size_t) all_files_size);
1339 if(!w->fds)
1340 error("Cannot allocate memory for fds in %s", w->name);
1341
@@ -1748,7 +1746,7 @@ void show_charts(void)
1746 for (w = target_root; w ; w = w->next) {
1747 if(w->target || (!w->processes && !w->exposed)) continue;
1748
1751 - fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz), w->hidden?"hidden,noreset":"noreset");
1749 + fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, Hertz, w->hidden ? "hidden,noreset" : "noreset");
1750 }
1751
1752 fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' apps apps stacked 20003 %d\n", update_every);
@@ -1915,14 +1913,15 @@ int main(int argc, char **argv)
1913
1914 procfile_adaptive_initial_allocation = 1;
1915
1918 - unsigned long started_t = time(NULL), current_t;
1916 + time_t started_t = time(NULL);
1917 + time_t current_t;
1918 Hertz = get_hertz();
1919 pid_max = get_pid_max();
1920 processors = get_processors();
1921
1922 parse_args(argc, argv);
1923
1925 - all_pids = calloc(sizeof(struct pid_stat *), pid_max);
1924 + all_pids = calloc(sizeof(struct pid_stat *), (size_t) pid_max);
1925 if(!all_pids) {
1926 error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
1927 printf("DISABLE\n");
@@ -1957,7 +1956,7 @@ int main(int argc, char **argv)
1956 if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
1957 else susec = update_every * 1000000ULL / 2;
1958
1960 - usleep(susec);
1959 + usleep((__useconds_t) susec);
1960 bcopy(&now, &last, sizeof(struct timeval));
1961
1962 current_t = time(NULL);
src/avl.c
+2 -2
@@ -319,7 +319,7 @@ int avl_removeroot(avl_tree* t) {
319 * returns the last value returned by iterator or 0 if there were no calls
320 * Warning: a<=b must hold
321 */
322 -int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
322 +int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) {
323 int x, c = 0;
324 if (!t->root)
325 return 0;
@@ -361,7 +361,7 @@ int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
361 return c;
362 }
363
364 -int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) {
364 +int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) {
365 #ifdef AVL_LOCK_WITH_MUTEX
366 pthread_mutex_lock(&t->mutex);
367 #else
src/avl.h
+4 -4
@@ -12,11 +12,11 @@
12 * Released under GNU General Public License (GPL) version 2
13 *
14 */
15 -#include <pthread.h>
16 -
15 #ifndef _AVL_H
16 #define _AVL_H 1
17
18 +#include <pthread.h>
19 +
20 // #define AVL_LOCK_WITH_MUTEX 1
21
22 /* Data structures */
@@ -65,13 +65,13 @@ int avl_removeroot(avl_tree* t);
65 * returns the last value returned by iterator or 0 if there were no calls
66 * Warning: a<=b must hold
67 */
68 -int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret);
68 +int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret);
69
70 /* Iterate through elements in t equal to a
71 * for each element calls iter(a) until it returns 0
72 * returns the last value returned by iterator or 0 if there were no calls
73 */
74 -int avl_search(avl_tree* t, avl* a, int (*iter)(avl* a), avl** ret);
74 +int avl_search(avl_tree* t, avl* a, int (*iter)(avl*), avl** ret);
75
76 /* Initialize the avl_tree
77 */
src/common.c
+3 -3
@@ -99,7 +99,7 @@ char *trim(char *s)
99 if(!*s || *s == '#') return NULL;
100
101 // skip tailing spaces
102 - int c = strlen(s) - 1;
102 + long c = (long) strlen(s) - 1;
103 while(c >= 0 && isspace(s[c])) {
104 s[c] = '\0';
105 c--;
@@ -109,7 +109,7 @@ char *trim(char *s)
109 return s;
110 }
111
112 -void *mymmap(const char *filename, unsigned long size, int flags, int ksm)
112 +void *mymmap(const char *filename, size_t size, int flags, int ksm)
113 {
114 int fd;
115 void *mem = NULL;
@@ -139,7 +139,7 @@ void *mymmap(const char *filename, unsigned long size, int flags, int ksm)
139 mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags|MAP_ANONYMOUS, -1, 0);
140 if(mem) {
141 if(lseek(fd, 0, SEEK_SET) == 0) {
142 - if(read(fd, mem, size) != size)
142 + if(read(fd, mem, size) != (ssize_t)size)
143 error("Cannot read from file '%s'", filename);
144 }
145 else
src/common.h
+2 -2
@@ -17,7 +17,7 @@ extern void strreverse(char* begin, char* end);
17 extern char *mystrsep(char **ptr, char *s);
18 extern char *trim(char *s);
19
20 -extern void *mymmap(const char *filename, unsigned long size, int flags, int ksm);
20 +extern void *mymmap(const char *filename, size_t size, int flags, int ksm);
21 extern int savememory(const char *filename, void *mem, unsigned long size);
22
23 extern int fd_is_valid(int fd);
@@ -26,7 +26,7 @@ extern char *global_host_prefix;
26 extern int enable_ksm;
27
28 /* Number of ticks per second */
29 -#define HZ hz
29 +#define HZ myhz
30 extern unsigned int hz;
31 extern void get_HZ(void);
32
src/daemon.c
+3 -3
@@ -137,7 +137,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch
137 fflush(NULL);
138
139 // open the files before forking
140 - int input_fd = -1, output_fd = -1, error_fd = -1, dev_null = -1;
140 + int input_fd = -1, output_fd = -1, error_fd = -1, dev_null;
141
142 if(input && *input) {
143 if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
@@ -244,7 +244,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch
244 // close all files
245 if(close_all_files) {
246 int i;
247 - for(i = sysconf(_SC_OPEN_MAX) - 1; i > 0; i--)
247 + for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
248 if(
249 ((access_fd && i != *access_fd) || !access_fd)
250 && i != dev_null
@@ -300,7 +300,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch
300 if(fd >= 0) {
301 char b[100];
302 sprintf(b, "%d\n", getpid());
303 - int i = write(fd, b, strlen(b));
303 + ssize_t i = write(fd, b, strlen(b));
304 if(i <= 0) perror("Cannot write pid to file.");
305 close(fd);
306 }
src/dictionary.c
+16 -4
@@ -40,7 +40,10 @@ static NAME_VALUE *dictionary_name_value_create(DICTIONARY *dict, const char *na
40 debug(D_DICTIONARY, "Creating name value entry for name '%s', value '%s'.", name, value);
41
42 NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
43 - if(!nv) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
43 + if(!nv) {
44 + fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
45 + exit(1);
46 + }
47
48 nv->name = strdup(name);
49 if(!nv->name) fatal("Cannot allocate name_value.name of size %z", strlen(name));
@@ -70,7 +73,10 @@ static void dictionary_name_value_destroy(DICTIONARY *dict, NAME_VALUE *nv) {
73 else {
74 NAME_VALUE *n = dict->values;
75 while(n && n->next && n->next != nv) nv = nv->next;
73 - if(!n || n->next != nv) fatal("Cannot find name_value with name '%s' in dictionary.", nv->name);
76 + if(!n || n->next != nv) {
77 + fatal("Cannot find name_value with name '%s' in dictionary.", nv->name);
78 + exit(1);
79 + }
80 n->next = nv->next;
81 nv->next = NULL;
82 }
@@ -86,7 +92,10 @@ DICTIONARY *dictionary_create(void) {
92 debug(D_DICTIONARY, "Creating dictionary.");
93
94 DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
89 - if(!dict) fatal("Cannot allocate DICTIONARY");
95 + if(!dict) {
96 + fatal("Cannot allocate DICTIONARY");
97 + exit(1);
98 + }
99
100 avl_init(&dict->values_index, name_value_compare);
101 pthread_rwlock_init(&dict->rwlock, NULL);
@@ -115,7 +124,10 @@ void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t val
124 if(!nv) {
125 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
126 nv = dictionary_name_value_create(dict, name, value, value_len);
118 - if(!nv) fatal("Cannot create name_value.");
127 + if(!nv) {
128 + fatal("Cannot create name_value.");
129 + exit(1);
130 + }
131 return nv->value;
132 }
133 else {
src/global_statistics.c
-1
@@ -3,7 +3,6 @@
3 #endif
4 #include <pthread.h>
5
6 -#include "common.h"
6 #include "global_statistics.h"
7
8 struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL };
src/log.c
-1
@@ -27,7 +27,6 @@ int access_log_syslog = 1;
27 int error_log_syslog = 1;
28 int output_log_syslog = 1; // debug log
29
30 -
30 void log_date(FILE *out)
31 {
32 char outstr[200];
src/main.c
+10 -11
@@ -158,7 +158,7 @@ void kill_childs()
158 if(tc_child_pid) {
159 info("Killing tc-qos-helper procees");
160 if(killpid(tc_child_pid, SIGTERM) != -1)
161 - waitid(P_PID, tc_child_pid, &info, WEXITED);
161 + waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
162 }
163 tc_child_pid = 0;
164
@@ -171,7 +171,7 @@ void kill_childs()
171 if(cd->pid && !cd->obsolete) {
172 debug(D_EXIT, "killing %s plugin process", cd->id);
173 if(killpid(cd->pid, SIGTERM) != -1)
174 - waitid(P_PID, cd->pid, &info, WEXITED);
174 + waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
175 }
176 }
177
@@ -317,7 +317,6 @@ int main(int argc, char **argv)
317
318 // --------------------------------------------------------------------
319
320 - silent = 0;
320 error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
321 if(strcmp(error_log_file, "syslog") == 0) {
322 error_log_syslog = 1;
@@ -326,7 +325,7 @@ int main(int argc, char **argv)
325 else if(strcmp(error_log_file, "none") == 0) {
326 error_log_syslog = 0;
327 error_log_file = NULL;
329 - silent = 1; // optimization - do not even generate debug log entries
328 + // optimization - do not even generate debug log entries
329 }
330 else error_log_syslog = 0;
331
@@ -356,7 +355,7 @@ int main(int argc, char **argv)
355
356 // --------------------------------------------------------------------
357
359 - rrd_default_history_entries = config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
358 + rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
359 if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
360 fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
361 rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
@@ -367,7 +366,7 @@ int main(int argc, char **argv)
366
367 // --------------------------------------------------------------------
368
370 - rrd_update_every = config_get_number("global", "update every", UPDATE_EVERY);
369 + rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
370 if(rrd_update_every < 1 || rrd_update_every > 600) {
371 fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", rrd_update_every, UPDATE_EVERY_MAX);
372 rrd_update_every = UPDATE_EVERY;
@@ -376,9 +375,9 @@ int main(int argc, char **argv)
375
376 // let the plugins know the min update_every
377 {
379 - char buffer[50];
380 - snprintf(buffer, 50, "%d", rrd_update_every);
381 - setenv("NETDATA_UPDATE_EVERY", buffer, 1);
378 + char buf[50];
379 + snprintf(buf, 50, "%d", rrd_update_every);
380 + setenv("NETDATA_UPDATE_EVERY", buf, 1);
381 }
382
383 // --------------------------------------------------------------------
@@ -398,9 +397,9 @@ int main(int argc, char **argv)
397
398 // --------------------------------------------------------------------
399
401 - listen_backlog = config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
400 + listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
401
403 - listen_port = config_get_number("global", "port", LISTEN_PORT);
402 + listen_port = (int) config_get_number("global", "port", LISTEN_PORT);
403 if(listen_port < 1 || listen_port > 65535) {
404 fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT);
405 listen_port = LISTEN_PORT;
src/plugin_idlejitter.c
+1 -1
@@ -28,7 +28,7 @@ void *cpuidlejitter_main(void *ptr)
28 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
29 error("Cannot set pthread cancel state to ENABLE.");
30
31 - int sleep_ms = config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
31 + int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
32 if(sleep_ms <= 0) {
33 config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
34 sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
src/plugin_proc.c
+1 -1
@@ -213,7 +213,7 @@ void *proc_main(void *ptr)
213 rrdset_done(stbytes);
214 }
215
216 - usleep(susec);
216 + usleep((useconds_t) susec);
217
218 // copy current to last
219 bcopy(&now, &last, sizeof(struct timeval));
src/plugin_proc.h
+1
@@ -16,5 +16,6 @@ extern int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt);
16 extern int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt);
17 extern int do_proc_interrupts(int update_every, unsigned long long dt);
18 extern int do_proc_softirqs(int update_every, unsigned long long dt);
19 +extern int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt);
20
21 #endif /* NETDATA_PLUGIN_PROC_H */
src/plugin_tc.c
+3 -3
@@ -153,7 +153,7 @@ static void tc_device_classes_cleanup(struct tc_device *d) {
153 static int cleanup_every = 999;
154
155 if(cleanup_every > 0) {
156 - cleanup_every = -config_get_number("plugin:tc", "cleanup unused classes every", 60);
156 + cleanup_every = (int) -config_get_number("plugin:tc", "cleanup unused classes every", 60);
157 if(cleanup_every > 0) cleanup_every = -cleanup_every;
158 if(cleanup_every == 0) cleanup_every = -1;
159 }
@@ -598,7 +598,7 @@ void *tc_main(void *ptr)
598 else if(first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0 && device && class) {
599 // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
600 if(words[1] && *words[1]) {
601 - class->bytes = atoll(words[1]);
601 + class->bytes = strtoull(words[1], NULL, 10);
602 class->updated = 1;
603 }
604 else class->bytes = 0;
@@ -673,7 +673,7 @@ void *tc_main(void *ptr)
673 return NULL;
674 }
675
676 - sleep(rrd_update_every);
676 + sleep((unsigned int) rrd_update_every);
677 }
678
679 return NULL;
src/plugins_d.c
+6 -6
@@ -396,10 +396,10 @@ void *pluginsd_worker_thread(void *arg)
396
397 if(unlikely(!count && cd->enabled)) {
398 error("PLUGINSD: '%s' (pid %d) does not generate usefull output. Waiting a bit before starting it again.", cd->fullfilename, cd->pid);
399 - sleep(cd->update_every * 10);
399 + sleep((unsigned int) (cd->update_every * 10));
400 }
401
402 - if(likely(cd->enabled)) sleep(cd->update_every);
402 + if(likely(cd->enabled)) sleep((unsigned int) cd->update_every);
403 else break;
404 }
405
@@ -421,7 +421,7 @@ void *pluginsd_main(void *ptr)
421
422 char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
423 int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
424 - int scan_frequency = config_get_number("plugins", "check for new plugins every", 60);
424 + int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
425 DIR *dir = NULL;
426 struct dirent *file = NULL;
427 struct plugind *cd;
@@ -447,7 +447,7 @@ void *pluginsd_main(void *ptr)
447
448 if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
449
450 - int len = strlen(file->d_name);
450 + int len = (int) strlen(file->d_name);
451 if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
452 if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
453 debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
@@ -484,7 +484,7 @@ void *pluginsd_main(void *ptr)
484 snprintf(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
485
486 cd->enabled = enabled;
487 - cd->update_every = config_get_number(cd->id, "update every", rrd_update_every);
487 + cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
488 cd->started_t = time(NULL);
489
490 char *def = "";
@@ -508,7 +508,7 @@ void *pluginsd_main(void *ptr)
508 }
509
510 closedir(dir);
511 - sleep(scan_frequency);
511 + sleep((unsigned int) scan_frequency);
512 }
513
514 return NULL;
src/popen.c
+2 -2
@@ -78,7 +78,7 @@ FILE *mypopen(const char *command, pid_t *pidptr)
78
79 // close all files
80 int i;
81 - for(i = sysconf(_SC_OPEN_MAX) - 1; i > 0; i--)
81 + for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
82 if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
83
84 // move the pipe to stdout
@@ -128,7 +128,7 @@ void mypclose(FILE *fp, pid_t pid) {
128 fclose(fp);
129
130 siginfo_t info;
131 - if(waitid(P_PID, pid, &info, WEXITED) != -1) {
131 + if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) {
132 switch(info.si_code) {
133 case CLD_EXITED:
134 error("pid %d exited with code %d.", info.si_pid, info.si_status);
src/proc_diskstats.c
+1 -4
@@ -12,10 +12,7 @@
12 #include "rrd.h"
13 #include "plugin_proc.h"
14
15 -#define RRD_TYPE_DISK "disk"
16 -#define RRD_TYPE_DISK_LEN strlen(RRD_TYPE_DISK)
17 -
18 -#define MAX_PROC_DISKSTATS_LINE 4096
15 +#define RRD_TYPE_DISK "disk"
16
17 int do_proc_diskstats(int update_every, unsigned long long dt) {
18 static procfile *ff = NULL;
src/procfile.c
+1 -2
@@ -235,7 +235,7 @@ cleanup:
235 procfile *procfile_readall(procfile *ff) {
236 debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
237
238 - ssize_t s, r = 1, x = ff->size;
238 + ssize_t s, r = 1, x;
239 ff->len = 0;
240
241 while(likely(r > 0)) {
@@ -253,7 +253,6 @@ procfile *procfile_readall(procfile *ff) {
253 }
254 ff = new;
255 ff->size += PROCFILE_INCREMENT_BUFFER;
256 - x = PROCFILE_INCREMENT_BUFFER;
256 }
257
258 debug(D_PROCFILE, "Reading file '%s', from position %ld with length %ld", ff->filename, s, ff->size - s);
src/rrd.c
+18 -16
@@ -437,7 +437,8 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
437 st->last_collected_time.tv_usec = 0;
438 st->counter_done = 0;
439
440 - st->gap_when_lost_iterations_above = config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2;
440 + st->gap_when_lost_iterations_above = (int) (
441 + config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
442
443 avl_init(&st->dimensions_index, rrddim_compare);
444
@@ -598,7 +599,7 @@ void rrddim_free(RRDSET *st, RRDDIM *rd)
599 {
600 debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
601
601 - RRDDIM *i = st->dimensions, *last = NULL;
602 + RRDDIM *i, *last = NULL;
603 for(i = st->dimensions; i && i != rd ; i = i->next) last = i;
604
605 if(!i) {
@@ -606,8 +607,9 @@ void rrddim_free(RRDSET *st, RRDDIM *rd)
607 return;
608 }
609
609 - if(last) last = i->next;
610 - else st->dimensions = i->next;
610 + if(last) last->next = rd->next;
611 + else st->dimensions = rd->next;
612 + rd->next = NULL;
613
614 rrddim_index_del(st, rd);
615
@@ -709,8 +711,8 @@ RRDSET *rrdset_find_bytype(const char *type, const char *id)
711 strncpy(buf, type, RRD_ID_LENGTH_MAX - 1);
712 buf[RRD_ID_LENGTH_MAX - 1] = '\0';
713 strcat(buf, ".");
712 - int len = strlen(buf);
713 - strncpy(&buf[len], id, RRD_ID_LENGTH_MAX - len);
714 + int len = (int) strlen(buf);
715 + strncpy(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
716 buf[RRD_ID_LENGTH_MAX] = '\0';
717
718 return(rrdset_find(buf));
@@ -850,8 +852,8 @@ unsigned long long rrdset_done(RRDSET *st)
852 // it is not the first entry
853 // calculate the proper last_collected_time, using usec_since_last_update
854 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
853 - st->last_collected_time.tv_sec = ut / 1000000ULL;
854 - st->last_collected_time.tv_usec = ut % 1000000ULL;
855 + st->last_collected_time.tv_sec = (__time_t) (ut / 1000000ULL);
856 + st->last_collected_time.tv_usec = (__suseconds_t) (ut % 1000000ULL);
857 }
858
859 // if this set has not been updated in the past
@@ -860,8 +862,8 @@ unsigned long long rrdset_done(RRDSET *st)
862 // it has never been updated before
863 // set a fake last_updated, in the past using usec_since_last_update
864 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
863 - st->last_updated.tv_sec = ut / 1000000ULL;
864 - st->last_updated.tv_usec = ut % 1000000ULL;
865 + st->last_updated.tv_sec = (__time_t) (ut / 1000000ULL);
866 + st->last_updated.tv_usec = (__suseconds_t) (ut % 1000000ULL);
867
868 // the first entry should not be stored
869 store_this_entry = 0;
@@ -879,8 +881,8 @@ unsigned long long rrdset_done(RRDSET *st)
881 gettimeofday(&st->last_collected_time, NULL);
882
883 unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
882 - st->last_updated.tv_sec = ut / 1000000ULL;
883 - st->last_updated.tv_usec = ut % 1000000ULL;
884 + st->last_updated.tv_sec = (__time_t) (ut / 1000000ULL);
885 + st->last_updated.tv_usec = (__suseconds_t) (ut % 1000000ULL);
886
887 // the first entry should not be stored
888 store_this_entry = 0;
@@ -1049,7 +1051,7 @@ unsigned long long rrdset_done(RRDSET *st)
1051 // it is now time to interpolate values on a second boundary
1052
1053 unsigned long long first_ut = last_ut;
1052 - int iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
1054 + long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
1055
1056 for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
1057 #ifdef NETDATA_INTERNAL_CHECKS
@@ -1061,7 +1063,7 @@ unsigned long long rrdset_done(RRDSET *st)
1063 debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
1064 }
1065
1064 - st->last_updated.tv_sec = next_ut / 1000000ULL;
1066 + st->last_updated.tv_sec = (__time_t) (next_ut / 1000000ULL);
1067 st->last_updated.tv_usec = 0;
1068
1069 for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
@@ -1084,8 +1086,8 @@ unsigned long long rrdset_done(RRDSET *st)
1086 , st->id, rd->name
1087 , new_value
1088 , rd->calculated_value
1087 - , (unsigned long long)(next_ut - last_ut)
1088 - , (unsigned long long)(now_ut - last_ut)
1089 + , (next_ut - last_ut)
1090 + , (now_ut - last_ut)
1091 );
1092
1093 rd->calculated_value -= new_value;
src/rrd.h
+1
@@ -1,5 +1,6 @@
1 #include <sys/time.h>
2 #include <pthread.h>
3 +#include <stdint.h>
4
5 #include "avl.h"
6 #include "storage_number.h"
src/rrd2json.c
+10 -10
@@ -2,9 +2,9 @@
2 #include <config.h>
3 #endif
4 #include <pthread.h>
5 -#include <sys/time.h>
5 #include <stdlib.h>
6 #include <string.h>
7 +#include <stdint.h>
8
9 #include "log.h"
10 #include "common.h"
@@ -270,8 +270,8 @@ typedef struct rrdresult {
270 RRDSET *st; // the chart this result refers to
271
272 int d; // the number of dimensions
273 - int n; // the number of values in the arrays
274 - int rows; // the number of rows used
273 + long n; // the number of values in the arrays
274 + long rows; // the number of rows used
275
276 uint8_t *od; // the options for the dimensions
277
@@ -279,10 +279,10 @@ typedef struct rrdresult {
279 calculated_number *v; // array n x d values
280 uint8_t *o; // array n x d options
281
282 - int c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
282 + long c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
283
284 - int group; // how many collected values were grouped for each row
285 - int update_every; // what is the suggested update frequency in seconds
284 + long group; // how many collected values were grouped for each row
285 + long update_every; // what is the suggested update frequency in seconds
286
287 calculated_number min;
288 calculated_number max;
@@ -449,7 +449,7 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options)
449
450 void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
451 {
452 - int rows = rrdr_rows(r);
452 + long rows = rrdr_rows(r);
453 long c, i;
454 RRDDIM *rd;
455
@@ -629,7 +629,7 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option
629 static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
630 {
631 //info("RRD2JSON(): %s: BEGIN", r->st->id);
632 - int row_annotations = 0, dates = JSON_DATES_JS, dates_with_new = 0;
632 + int row_annotations = 0, dates, dates_with_new = 0;
633 char kq[2] = "", // key quote
634 sq[2] = "", // string quote
635 pre_label[101] = "", // before each label
@@ -1051,7 +1051,7 @@ inline void rrdr_done(RRDR *r)
1051 r->c = 0;
1052 }
1053
1054 -static RRDR *rrdr_create(RRDSET *st, int n)
1054 +static RRDR *rrdr_create(RRDSET *st, long n)
1055 {
1056 if(unlikely(!st)) {
1057 error("NULL value given!");
@@ -1546,7 +1546,7 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
1546 return 200;
1547 }
1548
1549 -unsigned long rrd_stats_json(int type, RRDSET *st, BUFFER *wb, int points, int group, int group_method, time_t after, time_t before, int only_non_zero)
1549 +time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
1550 {
1551 int c;
1552 pthread_rwlock_rdlock(&st->rwlock);
src/rrd2json.h
+1 -1
@@ -57,7 +57,7 @@ extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb);
57
58 extern void rrd_stats_all_json(BUFFER *wb);
59
60 -extern unsigned long rrd_stats_json(int type, RRDSET *st, BUFFER *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero);
60 +extern time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long entries_to_show, long group, int group_method, time_t after, time_t before, int only_non_zero);
61
62 extern int rrd2format(RRDSET *st, BUFFER *out, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp);
63
src/storage_number.c
+3 -3
@@ -76,7 +76,7 @@ storage_number pack_storage_number(calculated_number value, uint32_t flags)
76 #ifdef STORAGE_WITH_MATH
77 // without this there are rounding problems
78 // example: 0.9 becomes 0.89
79 - r += lrint(n);
79 + r += lrint((double) n);
80 #else
81 r += (storage_number)n;
82 #endif
@@ -153,7 +153,7 @@ int print_calculated_number(char *str, calculated_number value)
153 #ifdef STORAGE_WITH_MATH
154 // without llrint() there are rounding problems
155 // for example 0.9 becomes 0.89
156 - unsigned long long uvalue = llrint(value * (calculated_number)100000);
156 + unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
157 #else
158 unsigned long long uvalue = value * (calculated_number)100000;
159 #endif
@@ -202,5 +202,5 @@ int print_calculated_number(char *str, calculated_number value)
202 else wstr[1] = '.';
203
204 // return the buffer length
205 - return ( (wstr - str) + 2 + decimal );
205 + return (int) ((wstr - str) + 2 + decimal );
206 }
src/web_buffer.c
+38 -45
@@ -52,7 +52,7 @@ void buffer_reset(BUFFER *wb)
52
53 const char *buffer_tostring(BUFFER *wb)
54 {
55 - buffer_need_bytes(wb, 1);
55 + buffer_need_bytes(wb, (size_t)1);
56 wb->buffer[wb->len] = '\0';
57
58 buffer_overflow_check(wb);
@@ -77,8 +77,7 @@ void buffer_strcat(BUFFER *wb, const char *txt)
77 {
78 if(unlikely(!txt || !*txt)) return;
79
80 - if(wb->size - wb->len < 512)
81 - buffer_need_bytes(wb, 512);
80 + buffer_need_bytes(wb, (size_t)(1));
81
82 char *s = &wb->buffer[wb->len], *end = &wb->buffer[wb->size];
83 long len = wb->len;
@@ -100,7 +99,7 @@ void buffer_strcat(BUFFER *wb, const char *txt)
99 else {
100 // terminate the string
101 // without increasing the length
103 - buffer_need_bytes(wb, 1);
102 + buffer_need_bytes(wb, (size_t)1);
103 wb->buffer[wb->len] = '\0';
104 }
105 }
@@ -126,8 +125,9 @@ void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
125 {
126 if(unlikely(!fmt || !*fmt)) return;
127
128 + buffer_need_bytes(wb, 1);
129 +
130 size_t len = wb->size - wb->len;
130 - if(unlikely(!len)) return;
131
132 wb->len += vsnprintf(&wb->buffer[wb->len], len, fmt, args);
133
@@ -140,20 +140,13 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
140 {
141 if(unlikely(!fmt || !*fmt)) return;
142
143 - if(unlikely(wb->len > wb->size)) {
144 - error("web_buffer_sprintf(): already overflown length %ld, size = %ld", wb->len, wb->size);
145 - }
146 -
147 -// if(wb->size - wb->len < 512)
148 -// web_buffer_need_bytes(wb, 512);
143 + buffer_need_bytes(wb, 1);
144
145 size_t len = wb->size - wb->len, wrote;
146
152 - buffer_need_bytes(wb, len);
153 -
147 va_list args;
148 va_start(args, fmt);
156 - wrote = vsnprintf(&wb->buffer[wb->len], len, fmt, args);
149 + wrote = (size_t) vsnprintf(&wb->buffer[wb->len], len, fmt, args);
150 va_end(args);
151
152 if(unlikely(wrote >= len)) {
@@ -207,25 +200,25 @@ void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minu
200 b[i++]='t';
201 b[i++]='e';
202 b[i++]='(';
210 - b[i++]= 48 + year / 1000; year -= (year / 1000) * 1000;
211 - b[i++]= 48 + year / 100; year -= (year / 100) * 100;
212 - b[i++]= 48 + year / 10;
213 - b[i++]= 48 + year % 10;
203 + b[i++]= (char) (48 + year / 1000); year -= (year / 1000) * 1000;
204 + b[i++]= (char) (48 + year / 100); year -= (year / 100) * 100;
205 + b[i++]= (char) (48 + year / 10);
206 + b[i++]= (char) (48 + year % 10);
207 b[i++]=',';
215 - b[i]= 48 + month / 10; if(b[i] != '0') i++;
216 - b[i++]= 48 + month % 10;
208 + b[i]= (char) (48 + month / 10); if(b[i] != '0') i++;
209 + b[i++]= (char) (48 + month % 10);
210 b[i++]=',';
218 - b[i]= 48 + day / 10; if(b[i] != '0') i++;
219 - b[i++]= 48 + day % 10;
211 + b[i]= (char) (48 + day / 10); if(b[i] != '0') i++;
212 + b[i++]= (char) (48 + day % 10);
213 b[i++]=',';
221 - b[i]= 48 + hours / 10; if(b[i] != '0') i++;
222 - b[i++]= 48 + hours % 10;
214 + b[i]= (char) (48 + hours / 10); if(b[i] != '0') i++;
215 + b[i++]= (char) (48 + hours % 10);
216 b[i++]=',';
224 - b[i]= 48 + minutes / 10; if(b[i] != '0') i++;
225 - b[i++]= 48 + minutes % 10;
217 + b[i]= (char) (48 + minutes / 10); if(b[i] != '0') i++;
218 + b[i++]= (char) (48 + minutes % 10);
219 b[i++]=',';
227 - b[i]= 48 + seconds / 10; if(b[i] != '0') i++;
228 - b[i++]= 48 + seconds % 10;
220 + b[i]= (char) (48 + seconds / 10); if(b[i] != '0') i++;
221 + b[i++]= (char) (48 + seconds % 10);
222 b[i++]=')';
223 b[i]='\0';
224
@@ -248,25 +241,25 @@ void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minute
241 char *b = &wb->buffer[wb->len];
242
243 int i = 0;
251 - b[i++]= 48 + year / 1000; year -= (year / 1000) * 1000;
252 - b[i++]= 48 + year / 100; year -= (year / 100) * 100;
253 - b[i++]= 48 + year / 10;
254 - b[i++]= 48 + year % 10;
244 + b[i++]= (char) (48 + year / 1000); year -= (year / 1000) * 1000;
245 + b[i++]= (char) (48 + year / 100); year -= (year / 100) * 100;
246 + b[i++]= (char) (48 + year / 10);
247 + b[i++]= (char) (48 + year % 10);
248 b[i++]='-';
256 - b[i++]= 48 + month / 10;
257 - b[i++]= 48 + month % 10;
249 + b[i++]= (char) (48 + month / 10);
250 + b[i++]= (char) (48 + month % 10);
251 b[i++]='-';
259 - b[i++]= 48 + day / 10;
260 - b[i++]= 48 + day % 10;
252 + b[i++]= (char) (48 + day / 10);
253 + b[i++]= (char) (48 + day % 10);
254 b[i++]=' ';
262 - b[i++]= 48 + hours / 10;
263 - b[i++]= 48 + hours % 10;
255 + b[i++]= (char) (48 + hours / 10);
256 + b[i++]= (char) (48 + hours % 10);
257 b[i++]=':';
265 - b[i++]= 48 + minutes / 10;
266 - b[i++]= 48 + minutes % 10;
258 + b[i++]= (char) (48 + minutes / 10);
259 + b[i++]= (char) (48 + minutes % 10);
260 b[i++]=':';
268 - b[i++]= 48 + seconds / 10;
269 - b[i++]= 48 + seconds % 10;
261 + b[i++]= (char) (48 + seconds / 10);
262 + b[i++]= (char) (48 + seconds % 10);
263 b[i]='\0';
264
265 wb->len += i;
@@ -313,15 +306,15 @@ void buffer_free(BUFFER *b)
306 free(b);
307 }
308
316 -void buffer_increase(BUFFER *b, long free_size_required)
309 +void buffer_increase(BUFFER *b, size_t free_size_required)
310 {
311 buffer_overflow_check(b);
312
320 - long left = b->size - b->len;
313 + size_t left = b->size - b->len;
314
315 if(left >= free_size_required) return;
316
324 - long increase = free_size_required - left;
317 + size_t increase = free_size_required - left;
318 if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
319
320 debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
src/web_buffer.h
+4 -4
@@ -8,8 +8,8 @@
8 #define WEB_DATA_LENGTH_INCREASE_STEP 16384
9
10 typedef struct web_buffer {
11 - long size; // allocation size of buffer
12 - long len; // current data length in buffer
11 + size_t size; // allocation size of buffer
12 + size_t len; // current data length in buffer
13 char *buffer; // the buffer
14 int contenttype;
15 time_t date; // the date this content has been generated
@@ -35,7 +35,7 @@ typedef struct web_buffer {
35 #define buffer_strlen(wb) ((wb)->len)
36 extern const char *buffer_tostring(BUFFER *wb);
37
38 -#define buffer_need_bytes(buffer, needed_free_size) do { if(unlikely((buffer)->size - (buffer)->len < (long)(needed_free_size))) buffer_increase((buffer), (long)(needed_free_size)); } while(0)
38 +#define buffer_need_bytes(buffer, needed_free_size) do { if(unlikely((buffer)->size - (buffer)->len < (size_t)(needed_free_size))) buffer_increase((buffer), (size_t)(needed_free_size)); } while(0)
39
40 #define buffer_flush(wb) wb->buffer[wb->len = 0] = '\0'
41 extern void buffer_reset(BUFFER *wb);
@@ -48,7 +48,7 @@ extern void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, i
48
49 extern BUFFER *buffer_create(long size);
50 extern void buffer_free(BUFFER *b);
51 -extern void buffer_increase(BUFFER *b, long free_size_required);
51 +extern void buffer_increase(BUFFER *b, size_t free_size_required);
52
53 extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...);
54 extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
src/web_client.c
+21 -20
@@ -436,7 +436,7 @@ uint32_t web_client_api_request_v1_data_options(char *o)
436 return ret;
437 }
438
439 -int web_client_api_request_v1_data_format(char *name)
439 +uint32_t web_client_api_request_v1_data_format(char *name)
440 {
441 if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
442 return DATASOURCE_DATATABLE_JSON;
@@ -471,7 +471,7 @@ int web_client_api_request_v1_data_format(char *name)
471 return DATASOURCE_JSON;
472 }
473
474 -int web_client_api_request_v1_data_google_format(char *name)
474 +uint32_t web_client_api_request_v1_data_google_format(char *name)
475 {
476 if(!strcmp(name, "json"))
477 return DATASOURCE_DATATABLE_JSONP;
@@ -579,7 +579,8 @@ int web_client_api_request_v1_data(struct web_client *w, char *url)
579 , *after_str = NULL
580 , *points_str = NULL;
581
582 - int format = DATASOURCE_JSON, group = GROUP_MAX;
582 + int group = GROUP_MAX;
583 + uint32_t format = DATASOURCE_JSON;
584 uint32_t options = 0x00000000;
585
586 while(url) {
@@ -842,7 +843,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
843 char *google_out = "json";
844 char *google_responseHandler = "google.visualization.Query.setResponse";
845 char *google_outFileName = NULL;
845 - unsigned long last_timestamp_in_data = 0;
846 + time_t last_timestamp_in_data = 0;
847 if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
848
849 w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
@@ -901,7 +902,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
902 }
903
904 debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %lu after, %lu before).", w->id, st->name, st->id, lines, group_count, group_method, after, before);
904 - unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero);
905 + time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero);
906
907 if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
908 if(timestamp_in_data > last_timestamp_in_data)
@@ -969,7 +970,7 @@ cleanup:
970
971 void web_client_process(struct web_client *w) {
972 int code = 500;
972 - int bytes;
973 + ssize_t bytes;
974
975 w->wait_receive = 0;
976
@@ -1179,7 +1180,7 @@ void web_client_process(struct web_client *w) {
1180 // prepare the HTTP response header
1181 debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
1182
1182 - char *content_type_string = "";
1183 + char *content_type_string;
1184 switch(w->response.data->contenttype) {
1185 case CT_TEXT_HTML:
1186 content_type_string = "text/html; charset=utf-8";
@@ -1243,7 +1244,7 @@ void web_client_process(struct web_client *w) {
1244 break;
1245 }
1246
1246 - char *code_msg = "";
1247 + char *code_msg;
1248 switch(code) {
1249 case 200:
1250 code_msg = "OK";
@@ -1308,7 +1309,7 @@ void web_client_process(struct web_client *w) {
1309 if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
1310 buffer_sprintf(w->response.header_output,
1311 "Content-Length: %ld\r\n"
1311 - , w->response.data->len?w->response.data->len:w->response.rlen
1312 + , w->response.data->len? w->response.data->len: w->response.rlen
1313 );
1314 else if(!w->response.zoutput)
1315 w->keepalive = 0; // content-length is required for keep-alive
@@ -1335,7 +1336,7 @@ void web_client_process(struct web_client *w) {
1336 );
1337
1338 bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
1338 - if(bytes != buffer_strlen(w->response.header_output))
1339 + if(bytes != (ssize_t) buffer_strlen(w->response.header_output))
1340 error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes)."
1341 , w->id
1342 , buffer_strlen(w->response.header_output)
@@ -1386,12 +1387,12 @@ void web_client_process(struct web_client *w) {
1387 }
1388 }
1389
1389 -long web_client_send_chunk_header(struct web_client *w, int len)
1390 +long web_client_send_chunk_header(struct web_client *w, long len)
1391 {
1392 debug(D_DEFLATE, "%llu: OPEN CHUNK of %d bytes (hex: %x).", w->id, len, len);
1393 char buf[1024];
1393 - sprintf(buf, "%X\r\n", len);
1394 - int bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT);
1394 + sprintf(buf, "%lX\r\n", len);
1395 + ssize_t bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT);
1396
1397 if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk header %d bytes.", w->id, bytes);
1398 else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk header to the client.", w->id);
@@ -1404,7 +1405,7 @@ long web_client_send_chunk_close(struct web_client *w)
1405 {
1406 //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
1407
1407 - int bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT);
1408 + ssize_t bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT);
1409
1410 if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
1411 else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
@@ -1417,7 +1418,7 @@ long web_client_send_chunk_finalize(struct web_client *w)
1418 {
1419 //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
1420
1420 - int bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT);
1421 + ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT);
1422
1423 if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
1424 else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
@@ -1479,7 +1480,7 @@ long web_client_send_deflate(struct web_client *w)
1480 // give the compressor all the data not passed through the compressor yet
1481 if(w->response.data->len > w->response.sent) {
1482 w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent];
1482 - w->response.zstream.avail_in = (w->response.data->len - w->response.sent);
1483 + w->response.zstream.avail_in = (uInt) (w->response.data->len - w->response.sent);
1484 }
1485
1486 // reset the compressor output buffer
@@ -1516,7 +1517,7 @@ long web_client_send_deflate(struct web_client *w)
1517 t += web_client_send_chunk_header(w, w->response.zhave);
1518 }
1519
1519 - len = send(w->ofd, &w->response.zbuffer[w->response.zsent], w->response.zhave - w->response.zsent, MSG_DONTWAIT);
1520 + len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
1521 if(len > 0) {
1522 w->response.zsent += len;
1523 if(t > 0) len += t;
@@ -1584,12 +1585,12 @@ long web_client_receive(struct web_client *w)
1585 long bytes;
1586
1587 if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
1587 - bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (left-1));
1588 + bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
1589 else
1589 - bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], left-1, MSG_DONTWAIT);
1590 + bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
1591
1592 if(likely(bytes > 0)) {
1592 - int old = w->response.data->len;
1593 + size_t old = w->response.data->len;
1594 w->response.data->len += bytes;
1595 w->response.data->buffer[w->response.data->len] = '\0';
1596
src/web_client.h
+2 -2
@@ -33,8 +33,8 @@ struct response {
33
34 int code; // the HTTP response code
35
36 - long rlen; // if non-zero, the excepted size of ifd (input)
37 - long sent; // current data length sent to output
36 + size_t rlen; // if non-zero, the excepted size of ifd (input)
37 + size_t sent; // current data length sent to output
38
39 int zoutput; // if set to 1, web_client_send() will send compressed data
40 #ifdef NETDATA_WITH_ZLIB
src/web_server.c
+3 -3
@@ -49,7 +49,7 @@ static void log_allocations(void)
49
50 int create_listen_socket4(int port, int listen_backlog)
51 {
52 - int sock = -1;
52 + int sock;
53 int sockopt = 1;
54 struct sockaddr_in name;
55
@@ -104,7 +104,7 @@ int create_listen_socket6(int port, int listen_backlog)
104
105 memset(&name, 0, sizeof(struct sockaddr_in6));
106 name.sin6_family = AF_INET6;
107 - name.sin6_port = htons (port);
107 + name.sin6_port = htons ((uint16_t) port);
108 name.sin6_addr = in6addr_any;
109 name.sin6_scope_id = 0;
110
@@ -151,7 +151,7 @@ void *socket_listen_main(void *ptr)
151 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
152 error("Cannot set pthread cancel state to ENABLE.");
153
154 - web_client_timeout = config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
154 + web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
155 web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
156
157 if(listen_fd < 0) fatal("LISTENER: Listen socket is not ready.");
web/dashboard.css
+14 -15
@@ -50,35 +50,32 @@ html {
50 color: #DDDDDD;
51 text-align: center;
52 overflow: hidden;
53 - z-index: 11;
53 + z-index: 20;
54 padding: 0px;
55 margin: 0px;
56 }
57
58 .netdata-message {
59 - display: none;
59 + display: inline-block;
60 + text-align: left;
61 + vertical-align: top;
62 + font-weight: bold;
63 font-size: x-small;
64 width: 100%;
62 - height: calc(100% - 10px); /* for the resize handler */
65 + height: 100%;
66 overflow: hidden;
67 background: inherit;
65 - z-index: 10000;
68 + z-index: 0;
69 }
70
68 -.netdata-chart-is-loading {
71 +.netdata-message.hidden {
72 display: none;
70 - font-size: x-small;
71 - width: 100%;
72 - overflow: hidden;
73 - background: white;
74 - z-index: 10000;
75 -}
76 -
77 -.netdata-error-message {
78 - background: #EEEEEE;
73 }
74
81 -.netdata-info-message {
75 +.netdata-message.icon {
76 + color: #FAFAFA;
77 + text-align: center;
78 + vertical-align: middle;
79 }
80
81 .netdata-chart-legend {
@@ -256,6 +253,7 @@ html {
253 overflow: hidden;
254 width: 100%;
255 height: 100%;
256 + z-index: 5;
257
258 /* width and height is calculated (depends on the appearance of the legend) */
259 }
@@ -269,6 +267,7 @@ html {
267 margin-right: 140px; /* --legend-width */
268 width: calc(100% - 140px); /* --legend-width */
269 height: 100%;
270 + z-index: 5;
271 flex-grow: 1;
272
273 /* width and height is calculated (depends on the appearance of the legend) */
web/dashboard.js
+1355 -1383
@@ -354,7 +354,7 @@
354 var a = i.split('.');
355
356 if(a[0] === 'options') {
357 - if(a[1] === 'options.setOptionCallback') continue;
357 + if(a[1] === 'setOptionCallback') continue;
358 if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
359 if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
360
@@ -386,9 +386,9 @@
386 // hidden all the not-visible charts
387 // using this little function we try to switch
388 // the charts back to visible quickly
389 - var targets = NETDATA.options.targets;
390 - var len = targets.length;
391 - while(len--) targets[len].isVisible();
389 + //var targets = NETDATA.options.targets;
390 + //var len = targets.length;
391 + //while(len--) targets[len].isVisible();
392 }
393
394 // ----------------------------------------------------------------------------------------------------------------
@@ -581,6 +581,7 @@
581 // dimensions selection
582
583 // FIXME
584 + // move color assignment to dimensions, here
585
586 dimensionStatus = function(parent, label, name_div, value_div, color) {
587 this.enabled = false;
@@ -777,17 +778,45 @@
778
779
780 // ----------------------------------------------------------------------------------------------------------------
780 - // Our state object, where all per-chart values are stored
781 + // global selection sync
782
782 - resizeCallback = function(state) {
783 - this.state = state;
784 - this.resize = function(height) {
785 - this.state
783 + NETDATA.globalSelectionSync = {
784 + state: null,
785 + dont_sync_before: 0,
786 + last_t: 0,
787 + slaves: [],
788 +
789 + stop: function() {
790 + if(this.state !== null)
791 + this.state.globalSelectionSyncStop();
792 + },
793 +
794 + delay: function() {
795 + if(this.state !== null) {
796 + this.state.globalSelectionSyncDelay();
797 + }
798 }
787 - }
799 + };
800 +
801 + // ----------------------------------------------------------------------------------------------------------------
802 + // Our state object, where all per-chart values are stored
803
804 chartState = function(element) {
805 var self = $(element);
806 + this.element = element;
807 +
808 + // IMPORTANT:
809 + // all private functions should use 'that', instead of 'this'
810 + var that = this;
811 +
812 + /* error() - private
813 + * show an error instead of the chart
814 + */
815 + var error = function(msg) {
816 + that.element.innerHTML = that.id + ': ' + msg;
817 + that.enabled = false;
818 + that.current = that.pan;
819 + }
820
821 // GUID - a unique identifier for the chart
822 this.uuid = NETDATA.guid();
@@ -803,1725 +832,1667 @@
832 this.height = self.data('height') || NETDATA.chartDefaults.height;
833
834 if(this.settings_id !== null) {
806 - var me = this;
835 this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
808 - me.resizeAndRedrawChart(height);
836 + // this is the callback that will be called
837 + // if and when the user resets all localStorage variables
838 + // to their defaults
839 +
840 + resizeChartToHeight(height);
841 });
842 }
843
812 - $.extend(this, {
813 - // string - the netdata server URL, without any path
814 - host: self.data('host') || NETDATA.chartDefaults.host,
815 -
816 - // string - the grouping method requested by the user
817 - method: self.data('method') || NETDATA.chartDefaults.method,
818 -
819 - // the time-range requested by the user
820 - after: self.data('after') || NETDATA.chartDefaults.after,
821 - before: self.data('before') || NETDATA.chartDefaults.before,
822 -
823 - // the pixels per point requested by the user
824 - pixels_per_point: self.data('pixels-per-point') || 1,
825 - points: self.data('points') || null,
826 -
827 - // the dimensions requested by the user
828 - dimensions: self.data('dimensions') || null,
829 -
830 - // the chart library requested by the user
831 - library_name: self.data('chart-library') || NETDATA.chartDefaults.library,
832 - library: null, // object - the chart library used
833 -
834 - colors: null,
835 - colors_assigned: {},
836 - colors_available: null,
837 -
838 - element: element, // the element already created by the user
839 - element_message: null,
840 - element_loading: null,
841 - element_chart: null, // the element with the chart
842 - element_chart_id: null,
843 - element_legend: null, // the element with the legend of the chart (if created by us)
844 - element_legend_id: null,
845 - element_legend_childs: {
846 - hidden: null,
847 - title_date: null,
848 - title_time: null,
849 - title_units: null,
850 - nano: null,
851 - nano_options: null,
852 - series: null
853 - },
854 -
855 - chart_url: null, // string - the url to download chart info
856 - chart: null, // object - the chart as downloaded from the server
857 -
858 - validated: false, // boolean - has the chart been validated?
859 - enabled: true, // boolean - is the chart enabled for refresh?
860 - paused: false, // boolean - is the chart paused for any reason?
861 - selected: false, // boolean - is the chart shown a selection?
862 - debug: false, // boolean - console.log() debug info about this chart
844 + // string - the netdata server URL, without any path
845 + this.host = self.data('host') || NETDATA.chartDefaults.host;
846
864 - dom_created: false, // boolean - is the DOM for the chart created?
865 - chart_created: false, // boolean - is the library.create() been called?
866 -
867 - updates_counter: 0, // numeric - the number of refreshes made so far
868 - updates_since_last_creation: 0,
847 + // make sure the host does not end with /
848 + // all netdata API requests use absolute paths
849 + while(this.host.slice(-1) === '/')
850 + this.host = this.host.substring(0, this.host.length - 1);
851
870 - tm: {
871 - last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
852 + // string - the grouping method requested by the user
853 + this.method = self.data('method') || NETDATA.chartDefaults.method;
854
873 - last_updated: 0, // the timestamp the chart last updated with data
855 + // the time-range requested by the user
856 + this.after = self.data('after') || NETDATA.chartDefaults.after;
857 + this.before = self.data('before') || NETDATA.chartDefaults.before;
858
875 - pan_and_zoom_seq: 0, // the sequence number of the global synchronization
876 - // between chart.
877 - // Used with NETDATA.globalPanAndZoom.seq
859 + // the pixels per point requested by the user
860 + this.pixels_per_point = self.data('pixels-per-point') || 1;
861 + this.points = self.data('points') || null;
862
879 - last_visible_check: 0, // the time we last checked if it is visible
863 + // the dimensions requested by the user
864 + this.dimensions = self.data('dimensions') || null;
865
881 - last_resized: 0, // the time the chart was resized
882 - last_hidden: 0, // the time the chart was hidden
883 - last_unhidden: 0, // the time the chart was unhidden
866 + // the chart library requested by the user
867 + this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library;
868
885 - last_autorefreshed: 0 // the time the chart was last refreshed
886 - },
869 + // object - the chart library used
870 + this.library = null;
871
888 - data: null, // the last data as downloaded from the netdata server
889 - data_url: 'invalid://', // string - the last url used to update the chart
890 - data_points: 0, // number - the number of points returned from netdata
891 - data_after: 0, // milliseconds - the first timestamp of the data
892 - data_before: 0, // milliseconds - the last timestamp of the data
893 - data_update_every: 0, // milliseconds - the frequency to update the data
894 - netdata_first: 0, // milliseconds - the first timestamp in netdata
895 - netdata_last: 0, // milliseconds - the last timestamp in netdata
896 -
897 - dimensions_visibility: new dimensionsVisibility(this),
898 -
899 - current: null, // auto, pan, zoom
900 - // this is a pointer to one of the sub-classes below
901 -
902 - auto: {
903 - name: 'auto',
904 - autorefresh: true,
905 - force_update_at: 0, // the timestamp to force the update at
906 - force_before_ms: null,
907 - force_after_ms: null,
908 - requested_before_ms: null,
909 - requested_after_ms: null,
910 - },
911 - pan: {
912 - name: 'pan',
913 - autorefresh: false,
914 - force_update_at: 0, // the timestamp to force the update at
915 - force_before_ms: null,
916 - force_after_ms: null,
917 - requested_before_ms: null,
918 - requested_after_ms: null,
919 - },
920 - zoom: {
921 - name: 'zoom',
922 - autorefresh: false,
923 - force_update_at: 0, // the timestamp to force the update at
924 - force_before_ms: null,
925 - force_after_ms: null,
926 - requested_before_ms: null,
927 - requested_after_ms: null,
928 - },
872 + // color management
873 + this.colors = null;
874 + this.colors_assigned = {};
875 + this.colors_available = null;
876
930 - refresh_dt_ms: 0, // milliseconds - the time the last refresh took
931 - refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms
932 - refresh_dt_element: null
933 - });
877 + // the element already created by the user
878 + this.element_message = null;
879
935 - this.init();
936 - }
880 + // the element with the chart
881 + this.element_chart = null;
882
938 - // ----------------------------------------------------------------------------------------------------------------
939 - // Chart Resize
940 -
941 - // this is actual chart resize algorithm
942 - // it will:
943 - // - resize the entire container
944 - // - update the internal states
945 - // - resize the chart as the div changes height
946 - // - update the scrollbar of the legend
947 - chartState.prototype.resizeAndRedrawChart = function(h) {
948 - // console.log(h);
949 - this.element.style.height = h;
950 -
951 - if(this.settings_id !== null)
952 - NETDATA.localStorageSet('chart_heights.' + this.settings_id, h);
953 -
954 - var now = new Date().getTime();
955 - NETDATA.options.last_page_scroll = now;
956 - NETDATA.options.last_resized = now;
957 - NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
958 -
959 - if(typeof this.library.resize === 'function' && this.element_chart !== null)
960 - this.library.resize(this);
961 -
962 - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
963 - $(this.element_legend_childs.nano).nanoScroller();
964 - };
883 + // the element with the legend of the chart (if created by us)
884 + this.element_legend = null;
885 + this.element_legend_childs = {
886 + hidden: null,
887 + title_date: null,
888 + title_time: null,
889 + title_units: null,
890 + nano: null,
891 + nano_options: null,
892 + series: null
893 + };
894
966 - chartState.prototype.resizeHandler = function(e) {
967 - e.preventDefault();
895 + this.chart_url = null; // string - the url to download chart info
896 + this.chart = null; // object - the chart as downloaded from the server
897 +
898 + this.validated = false; // boolean - has the chart been validated?
899 + this.enabled = true; // boolean - is the chart enabled for refresh?
900 + this.paused = false; // boolean - is the chart paused for any reason?
901 + this.selected = false; // boolean - is the chart shown a selection?
902 + this.debug = false; // boolean - console.log() debug info about this chart
903 +
904 + this.netdata_first = 0; // milliseconds - the first timestamp in netdata
905 + this.netdata_last = 0; // milliseconds - the last timestamp in netdata
906 + this.requested_after = null; // milliseconds - the timestamp of the request after param
907 + this.requested_before = null; // milliseconds - the timestamp of the request before param
908 +
909 + this.auto = {
910 + name: 'auto',
911 + autorefresh: true,
912 + force_update_at: 0, // the timestamp to force the update at
913 + force_before_ms: null,
914 + force_after_ms: null
915 + };
916 + this.pan = {
917 + name: 'pan',
918 + autorefresh: false,
919 + force_update_at: 0, // the timestamp to force the update at
920 + force_before_ms: null,
921 + force_after_ms: null
922 + };
923 + this.zoom = {
924 + name: 'zoom',
925 + autorefresh: false,
926 + force_update_at: 0, // the timestamp to force the update at
927 + force_before_ms: null,
928 + force_after_ms: null
929 + };
930
969 - if(typeof this.event_resize === 'undefined'
970 - || this.event_resize.chart_original_w === 'undefined'
971 - || this.event_resize.chart_original_h === 'undefined')
972 - this.event_resize = {
973 - chart_original_w: this.element.clientWidth,
974 - chart_original_h: this.element.clientHeight,
975 - last: 0
976 - };
931 + // this is a pointer to one of the sub-classes below
932 + // auto, pan, zoom
933 + this.current = this.auto;
934
978 - if(e.type === 'touchstart') {
979 - this.event_resize.mouse_start_x = e.touches.item(0).pageX;
980 - this.event_resize.mouse_start_y = e.touches.item(0).pageY;
935 + // check the requested library is available
936 + // we don't initialize it here - it will be initialized when
937 + // this chart will be first used
938 + if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') {
939 + NETDATA.error(402, that.library_name);
940 + error('chart library "' + that.library_name + '" is not found');
941 + return;
942 }
982 - else {
983 - this.event_resize.mouse_start_x = e.clientX;
984 - this.event_resize.mouse_start_y = e.clientY;
943 + else if(NETDATA.chartLibraries[that.library_name].enabled === false) {
944 + NETDATA.error(403, that.library_name);
945 + error('chart library "' + that.library_name + '" is not enabled');
946 + return;
947 }
948 + else
949 + that.library = NETDATA.chartLibraries[that.library_name];
950
987 - this.event_resize.chart_start_w = this.element.clientWidth;
988 - this.event_resize.chart_start_h = this.element.clientHeight;
989 - this.event_resize.chart_last_w = this.element.clientWidth;
990 - this.event_resize.chart_last_h = this.element.clientHeight;
951 + // milliseconds - the time the last refresh took
952 + this.refresh_dt_ms = 0;
953
992 - var now = new Date().getTime();
993 - if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
994 - // double click / double tap event
954 + // if we need to report the rendering speed
955 + // find the element that needs to be updated
956 + var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms
957
996 - // the optimal height of the chart
997 - // showing the entire legend
998 - var optimal = this.event_resize.chart_last_h
999 - + this.element_legend_childs.content.scrollHeight
1000 - - this.element_legend_childs.content.clientHeight;
958 + if(refresh_dt_element_name !== null)
959 + this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
960 + else
961 + this.refresh_dt_element = null;
962
1002 - // if we are not optimal, be optimal
1003 - if(this.event_resize.chart_last_h != optimal)
1004 - this.resizeAndRedrawChart(optimal.toString() + 'px');
963 + this.dimensions_visibility = new dimensionsVisibility(this);
964
1006 - // else if we do not have the original height
1007 - // reset to the original height
1008 - else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1009 - this.resizeAndRedrawChart(this.event_resize.chart_original_h.toString() + 'px');
1010 - }
1011 - else {
1012 - this.event_resize.last = now;
1013 - var self = this;
965 + // ============================================================================================================
966 + // PRIVATE FUNCTIONS
967
1015 - // process movement event
1016 - document.onmousemove =
1017 - document.ontouchmove =
1018 - this.element_legend_childs.resize_handler.onmousemove =
1019 - this.element_legend_childs.resize_handler.ontouchmove =
1020 - function(e) {
1021 - var y = null;
1022 -
1023 - switch(e.type) {
1024 - case 'mousemove': y = e.clientY; break;
1025 - case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1026 - }
968 + var createDOM = function() {
969 + if(that.enabled == false) return;
970
1028 - if(y !== null) {
1029 - var newH = self.event_resize.chart_start_h + y - self.event_resize.mouse_start_y;
971 + if(that.element_message !== null) that.element_message.innerHTML = '';
972 + if(that.element_legend !== null) that.element_legend.innerHTML = '';
973 + if(that.element_chart !== null) that.element_chart.innerHTML = '';
974
1031 - if(newH >= 70 && newH !== self.event_resize.chart_last_h) {
1032 - self.resizeAndRedrawChart(newH.toString() + 'px');
1033 - self.event_resize.chart_last_h = newH;
1034 - }
1035 - }
1036 - };
975 + that.element.innerHTML = '';
976
1038 - // process end event
1039 - document.onmouseup =
1040 - document.ontouchend =
1041 - this.element_legend_childs.resize_handler.onmouseup =
1042 - this.element_legend_childs.resize_handler.ontouchend =
1043 - function(e) {
1044 - // remove all the hooks
1045 - document.onmouseup =
1046 - document.onmousemove =
1047 - document.ontouchmove =
1048 - document.ontouchend =
1049 - self.element_legend_childs.resize_handler.onmousemove =
1050 - self.element_legend_childs.resize_handler.ontouchmove =
1051 - self.element_legend_childs.resize_handler.onmouseout =
1052 - self.element_legend_childs.resize_handler.onmouseup =
1053 - self.element_legend_childs.resize_handler.ontouchend =
1054 - null;
1055 -
1056 - // allow auto-refreshes
1057 - NETDATA.options.auto_refresher_stop_until = 0;
1058 - };
1059 - }
1060 - }
977 + that.element_message = document.createElement('div');
978 + that.element_message.className = ' netdata-message hidden';
979 + that.element.appendChild(that.element_message);
980
1062 -
1063 - // ----------------------------------------------------------------------------------------------------------------
1064 - // global selection sync
981 + that.element_chart = document.createElement('div');
982 + that.element_chart.id = that.library_name + '-' + that.uuid + '-chart';
983 + that.element.appendChild(that.element_chart);
984
1066 - NETDATA.globalSelectionSync = {
1067 - state: null,
1068 - dont_sync_before: 0,
1069 - slaves: []
1070 - };
985 + if(that.hasLegend() === true) {
986 + that.element.className = "netdata-container-with-legend";
987 + that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right';
988
1072 - // prevent to global selection sync for some time
1073 - chartState.prototype.globalSelectionSyncDelay = function(ms) {
1074 - if(NETDATA.options.current.sync_selection === false)
1075 - return;
989 + that.element_legend = document.createElement('div');
990 + that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend';
991 + that.element.appendChild(that.element_legend);
992 + }
993 + else {
994 + that.element.className = "netdata-container";
995 + that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart';
996
1077 - if(typeof ms === 'number')
1078 - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1079 - else
1080 - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1081 - }
997 + that.element_legend = null;
998 + }
999 + that.element_legend_childs.series = null;
1000
1083 - // can we globally apply selection sync?
1084 - chartState.prototype.globalSelectionSyncAbility = function() {
1085 - if(NETDATA.options.current.sync_selection === false)
1086 - return false;
1001 + if(that.width !== 0)
1002 + $(that.element).css('width', that.width);
1003
1088 - if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) return false;
1089 - return true;
1090 - }
1004 + if(that.height !== 0)
1005 + $(that.element).css('height', that.height);
1006
1092 - chartState.prototype.globalSelectionSyncIsMaster = function() {
1093 - if(NETDATA.globalSelectionSync.state === this)
1094 - return true;
1095 - else
1096 - return false;
1097 - }
1007 + if(NETDATA.chartDefaults.min_width !== null)
1008 + $(that.element).css('min-width', NETDATA.chartDefaults.min_width);
1009
1099 - // this chart is the master of the global selection sync
1100 - chartState.prototype.globalSelectionSyncBeMaster = function() {
1101 - // am I the master?
1102 - if(this.globalSelectionSyncIsMaster()) {
1103 - if(this.debug === true)
1104 - this.log('sync: I am the master already.');
1010 + that.tm.last_dom_created = new Date().getTime();
1011
1106 - return;
1012 + showLoading();
1013 }
1014
1109 - if(NETDATA.globalSelectionSync.state) {
1110 - if(this.debug === true)
1111 - this.log('sync: I am not the sync master. Resetting global sync.');
1015 + /* init() private
1016 + * initialize state viariables
1017 + * destroy all (possibly) created state elements
1018 + * create the basic DOM for a chart
1019 + */
1020 + var init = function() {
1021 + if(that.enabled == false) return;
1022
1113 - this.globalSelectionSyncStop();
1114 - }
1023 + that.paused = false;
1024 + that.selected = false;
1025
1116 - // become the master
1117 - if(this.debug === true)
1118 - this.log('sync: becoming sync master.');
1026 + that.chart_created = false; // boolean - is the library.create() been called?
1027 + that.updates_counter = 0; // numeric - the number of refreshes made so far
1028 + that.updates_since_last_creation = 0;
1029
1120 - this.selected = true;
1121 - NETDATA.globalSelectionSync.state = this;
1030 + that.tm = {
1031 + last_initialized: 0, // milliseconds - the timestamp it was last initialized
1032 + last_dom_created: 0, // milliseconds - the timestamp its DOM was last created
1033 + last_mode_switch: 0, // milliseconds - the timestamp it switched modes
1034
1123 - // find the all slaves
1124 - var targets = NETDATA.options.targets;
1125 - var len = targets.length;
1126 - while(len--) {
1127 - st = targets[len];
1035 + last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart
1036 + last_updated: 0, // the timestamp the chart last updated with data
1037 + pan_and_zoom_seq: 0, // the sequence number of the global synchronization
1038 + // between chart.
1039 + // Used with NETDATA.globalPanAndZoom.seq
1040 + last_visible_check: 0, // the time we last checked if it is visible
1041 + last_resized: 0, // the time the chart was resized
1042 + last_hidden: 0, // the time the chart was hidden
1043 + last_unhidden: 0, // the time the chart was unhidden
1044 + last_autorefreshed: 0 // the time the chart was last refreshed
1045 + },
1046
1129 - if(st === this) {
1130 - if(this.debug === true)
1131 - st.log('sync: not adding me to sync');
1132 - }
1133 - else if(st.globalSelectionSyncIsEligible()) {
1134 - if(this.debug === true)
1135 - st.log('sync: adding to sync as slave');
1047 + that.data = null; // the last data as downloaded from the netdata server
1048 + that.data_url = 'invalid://'; // string - the last url used to update the chart
1049 + that.data_points = 0; // number - the number of points returned from netdata
1050 + that.data_after = 0; // milliseconds - the first timestamp of the data
1051 + that.data_before = 0; // milliseconds - the last timestamp of the data
1052 + that.data_update_every = 0; // milliseconds - the frequency to update the data
1053
1137 - st.globalSelectionSyncBeSlave();
1138 - }
1054 + that.tm.last_initialized = new Date().getTime();
1055 + createDOM();
1056 +
1057 + that.setMode('auto');
1058 }
1059
1141 - // this.globalSelectionSyncDelay(100);
1142 - }
1060 + var maxMessageFontSize = function() {
1061 + // normally we want a font size, as tall as the element
1062 + var h = that.element_message.clientHeight;
1063
1144 - // can the chart participate to the global selection sync as a slave?
1145 - chartState.prototype.globalSelectionSyncIsEligible = function() {
1146 - if(this.enabled === true
1147 - && this.library !== null
1148 - && typeof this.library.setSelection === 'function'
1149 - && this.isVisible()
1150 - && this.dom_created === true
1151 - && this.chart_created === true)
1152 - return true;
1064 + // but give it some air, 20% let's say, or 5 pixels min
1065 + var lost = Math.max(h * 0.2, 5);
1066 + h -= lost;
1067
1154 - return false;
1155 - }
1068 + // center the text, verically
1069 + var paddingTop = (lost - 5) / 2;
1070
1157 - // this chart is a slave of the global selection sync
1158 - chartState.prototype.globalSelectionSyncBeSlave = function() {
1159 - if(NETDATA.globalSelectionSync.state !== this)
1160 - NETDATA.globalSelectionSync.slaves.push(this);
1161 - }
1071 + // but check the width too
1072 + // it should fit 10 characters in it
1073 + var w = that.element_message.clientWidth / 10;
1074 + if(h > w) {
1075 + paddingTop += (h - w) / 2;
1076 + h = w;
1077 + }
1078
1163 - // sync all the visible charts to the given time
1164 - // this is to be called from the chart libraries
1165 - chartState.prototype.globalSelectionSync = function(t) {
1166 - if(this.globalSelectionSyncAbility() === false) {
1167 - if(this.debug === true)
1168 - this.log('sync: cannot sync (yet?).');
1079 + // and don't make it too huge
1080 + // 5% of the screen size is good
1081 + if(h > screen.height / 20) {
1082 + paddingTop += (h - (screen.height / 20)) / 2;
1083 + h = screen.height / 20;
1084 + }
1085
1170 - return;
1086 + // set it
1087 + that.element_message.style.fontSize = h.toString() + 'px';
1088 + that.element_message.style.paddingTop = paddingTop.toString() + 'px';
1089 }
1090
1173 - if(this.globalSelectionSyncIsMaster() === false) {
1174 - if(this.debug === true)
1175 - this.log('sync: trying to be sync master.');
1176 -
1177 - this.globalSelectionSyncBeMaster();
1091 + var showMessage = function(msg) {
1092 + that.element_message.className = 'netdata-message';
1093 + that.element_message.innerHTML = msg;
1094 + this.element_message.style.fontSize = 'x-small';
1095 + that.element_message.style.paddingTop = '0px';
1096 + that.___messageHidden___ = undefined;
1097 + }
1098
1179 - if(this.globalSelectionSyncAbility() === false) {
1180 - if(this.debug === true)
1181 - this.log('sync: cannot sync (yet?).');
1099 + var showMessageIcon = function(icon) {
1100 + that.element_message.innerHTML = icon;
1101 + that.element_message.className = 'netdata-message icon';
1102 + maxMessageFontSize();
1103 + that.___messageHidden___ = undefined;
1104 + }
1105
1183 - return;
1106 + var hideMessage = function() {
1107 + if(typeof that.___messageHidden___ === 'undefined') {
1108 + that.___messageHidden___ = true;
1109 + that.element_message.className = 'netdata-message hidden';
1110 }
1111 }
1112
1187 - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1188 - st.setSelection(t);
1189 - });
1190 - }
1191 -
1192 - // stop syncing all charts to the given time
1193 - chartState.prototype.globalSelectionSyncStop = function() {
1194 - if(NETDATA.globalSelectionSync.slaves.length) {
1195 - if(this.debug === true)
1196 - this.log('sync: cleaning up...');
1197 -
1198 - var self = this;
1199 - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1200 - if(st === self) {
1201 - if(self.debug === true)
1202 - st.log('sync: not adding me to sync stop');
1203 - }
1204 - else {
1205 - if(self.debug === true)
1206 - st.log('sync: removed slave from sync');
1207 -
1208 - st.clearSelection();
1209 - }
1210 - });
1113 + var showRendering = function() {
1114 + var icon;
1115 + if(that.chart !== null) {
1116 + if(that.chart.chart_type === 'line')
1117 + icon = '<i class="fa fa-line-chart"></i>';
1118 + else
1119 + icon = '<i class="fa fa-area-chart"></i>';
1120 + }
1121 + else
1122 + icon = '<i class="fa fa-area-chart"></i>';
1123
1212 - NETDATA.globalSelectionSync.slaves = [];
1213 - NETDATA.globalSelectionSync.state = null;
1124 + showMessageIcon(icon + ' netdata');
1125 }
1126
1216 - // since we are the sync master, we should not call this.clearSelection()
1217 - // dygraphs is taking care of visualizing our selection.
1218 - this.selected = false;
1219 - }
1220 -
1221 - chartState.prototype.setSelection = function(t) {
1222 - if(typeof this.library.setSelection === 'function') {
1223 - if(this.library.setSelection(this, t) === true)
1224 - this.selected = true;
1225 - else
1226 - this.selected = false;
1127 + var showLoading = function() {
1128 + if(that.chart_created === false) {
1129 + showMessageIcon('<i class="fa fa-refresh"></i> netdata');
1130 + return true;
1131 + }
1132 + return false;
1133 }
1228 - else this.selected = true;
1134
1230 - if(this.selected === true && this.debug === true)
1231 - this.log('selection set to ' + t.toString());
1135 + // hide the chart, when it is not visible - called from isVisible()
1136 + var hideChart = function() {
1137 + // no chart yet
1138 + if(that.chart_created === false) return;
1139
1233 - return this.selected;
1234 - }
1140 + // we should destroy it
1141 + if(NETDATA.options.current.destroy_on_hide === true) {
1142 + init();
1143 + that.___chartIsHidden___ = undefined;
1144 + return;
1145 + }
1146
1236 - chartState.prototype.clearSelection = function() {
1237 - if(this.selected === true) {
1238 - if(typeof this.library.clearSelection === 'function') {
1239 - if(this.library.clearSelection(this) === true)
1240 - this.selected = false;
1241 - else
1242 - this.selected = true;
1147 + // just hide it, if it is not already hidden
1148 + if(typeof that.___chartIsHidden___ === 'undefined') {
1149 + showRendering();
1150 + that.element_chart.style.display = 'none';
1151 + if(that.element_legend !== null) that.element_legend.style.display = 'none';
1152 + that.___chartIsHidden___ = true;
1153 + }
1154 + }
1155 +
1156 + // unhide the chart, when it is visible - called from isVisible()
1157 + var unhideChart = function() {
1158 + if(typeof that.___chartIsHidden___ !== 'undefined') {
1159 + that.element_chart.style.display = 'inline-block';
1160 + if(that.element_legend !== null) that.element_legend.style.display = 'inline-block';
1161 + hideMessage();
1162 + that.___chartIsHidden___ = undefined;
1163 + resizeChart();
1164 }
1244 - else this.selected = false;
1245 -
1246 - if(this.selected === false && this.debug === true)
1247 - this.log('selection cleared');
1165 }
1166
1250 - this.legendReset();
1251 - return this.selected;
1252 - }
1253 -
1254 - // find if a timestamp (ms) is shown in the current chart
1255 - chartState.prototype.timeIsVisible = function(t) {
1256 - if(t >= this.data_after && t <= this.data_before)
1257 - return true;
1258 - return false;
1259 - },
1167 + // ----------------------------------------------------------------------------------------------------------------
1168 + // Chart Resize
1169
1261 - chartState.prototype.calculateRowForTime = function(t) {
1262 - if(this.timeIsVisible(t) === false) return -1;
1263 - return Math.floor((t - this.data_after) / this.data_update_every);
1264 - }
1170 + // resizeChart() - private
1171 + // to be called just before the chart library to make sure that
1172 + // a properly sized dom is available
1173 + var resizeChart = function() {
1174 + if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) {
1175 + that.tm.last_resized = new Date().getTime();
1176 + if(that.chart_created === false) return;
1177
1266 - // ----------------------------------------------------------------------------------------------------------------
1178 + if(that.needsRecreation())
1179 + init();
1180
1268 - // console logging
1269 - chartState.prototype.log = function(msg) {
1270 - console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1271 - }
1181 + else if(typeof that.library.resize === 'function') {
1182 + that.library.resize(that);
1183
1273 - chartState.prototype.pauseChart = function() {
1274 - if(this.paused === false) {
1275 - if(this.debug === true)
1276 - this.log('paused');
1184 + if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
1185 + $(that.element_legend_childs.nano).nanoScroller();
1186
1278 - this.paused = true;
1187 + maxMessageFontSize();
1188 + }
1189 + }
1190 }
1280 - }
1281 -
1282 - chartState.prototype.unpauseChart = function() {
1283 - if(this.paused) {
1284 - if(this.debug === true)
1285 - this.log('unpaused');
1191
1287 - this.paused = false;
1288 - }
1289 - }
1192 + // this is the actual chart resize algorithm
1193 + // it will:
1194 + // - resize the entire container
1195 + // - update the internal states
1196 + // - resize the chart as the div changes height
1197 + // - update the scrollbar of the legend
1198 + var resizeChartToHeight = function(h) {
1199 + // console.log(h);
1200 + that.element.style.height = h;
1201
1291 - chartState.prototype.resetChart = function() {
1292 - if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible())
1293 - NETDATA.globalPanAndZoom.clearMaster();
1202 + if(that.settings_id !== null)
1203 + NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
1204
1295 - this.tm.pan_and_zoom_seq = 0;
1205 + var now = new Date().getTime();
1206 + NETDATA.options.last_page_scroll = now;
1207 + NETDATA.options.last_resized = now;
1208 + NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
1209
1297 - this.clearSelection();
1210 + // force a resize
1211 + that.tm.last_resized = 0;
1212 + resizeChart();
1213 + };
1214
1299 - this.setMode('auto');
1300 - this.current.force_update_at = 0;
1301 - this.current.force_before_ms = null;
1302 - this.current.force_after_ms = null;
1303 - this.tm.last_autorefreshed = 0;
1304 - this.paused = false;
1305 - this.selected = false;
1306 - this.enabled = true;
1307 - this.debug = false;
1308 -
1309 - // do not update the chart here
1310 - // or the chart will flip-flop when it is the master
1311 - // of a selection sync and another chart becomes
1312 - // the new master
1313 - if(NETDATA.options.current.sync_pan_and_zoom === false && this.isVisible() === true)
1314 - this.updateChart();
1315 - }
1215 + this.resizeHandler = function(e) {
1216 + e.preventDefault();
1217
1317 - chartState.prototype.setMode = function(m) {
1318 - if(this.current) {
1319 - if(this.current.name === m) return;
1218 + if(typeof this.event_resize === 'undefined'
1219 + || this.event_resize.chart_original_w === 'undefined'
1220 + || this.event_resize.chart_original_h === 'undefined')
1221 + this.event_resize = {
1222 + chart_original_w: this.element.clientWidth,
1223 + chart_original_h: this.element.clientHeight,
1224 + last: 0
1225 + };
1226
1321 - this[m].requested_before_ms = this.current.requested_before_ms;
1322 - this[m].requested_after_ms = this.current.requested_after_ms;
1323 - }
1227 + if(e.type === 'touchstart') {
1228 + this.event_resize.mouse_start_x = e.touches.item(0).pageX;
1229 + this.event_resize.mouse_start_y = e.touches.item(0).pageY;
1230 + }
1231 + else {
1232 + this.event_resize.mouse_start_x = e.clientX;
1233 + this.event_resize.mouse_start_y = e.clientY;
1234 + }
1235
1325 - if(m === 'auto')
1326 - this.current = this.auto;
1327 - else if(m === 'pan')
1328 - this.current = this.pan;
1329 - else if(m === 'zoom')
1330 - this.current = this.zoom;
1331 - else
1332 - this.current = this.auto;
1236 + this.event_resize.chart_start_w = this.element.clientWidth;
1237 + this.event_resize.chart_start_h = this.element.clientHeight;
1238 + this.event_resize.chart_last_w = this.element.clientWidth;
1239 + this.event_resize.chart_last_h = this.element.clientHeight;
1240
1334 - this.current.force_update_at = 0;
1335 - this.current.force_before_ms = null;
1336 - this.current.force_after_ms = null;
1241 + var now = new Date().getTime();
1242 + if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
1243 + // double click / double tap event
1244 +
1245 + // the optimal height of the chart
1246 + // showing the entire legend
1247 + var optimal = this.event_resize.chart_last_h
1248 + + this.element_legend_childs.content.scrollHeight
1249 + - this.element_legend_childs.content.clientHeight;
1250 +
1251 + // if we are not optimal, be optimal
1252 + if(this.event_resize.chart_last_h != optimal)
1253 + resizeChartToHeight(optimal.toString() + 'px');
1254 +
1255 + // else if we do not have the original height
1256 + // reset to the original height
1257 + else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
1258 + resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
1259 + }
1260 + else {
1261 + this.event_resize.last = now;
1262 +
1263 + // process movement event
1264 + document.onmousemove =
1265 + document.ontouchmove =
1266 + this.element_legend_childs.resize_handler.onmousemove =
1267 + this.element_legend_childs.resize_handler.ontouchmove =
1268 + function(e) {
1269 + var y = null;
1270 +
1271 + switch(e.type) {
1272 + case 'mousemove': y = e.clientY; break;
1273 + case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
1274 + }
1275
1338 - if(this.debug === true)
1339 - this.log('mode set to ' + this.current.name);
1340 - }
1276 + if(y !== null) {
1277 + var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
1278
1342 - chartState.prototype.updateChartPanOrZoom = function(after, before) {
1343 - if(before < after) return false;
1279 + if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
1280 + resizeChartToHeight(newH.toString() + 'px');
1281 + that.event_resize.chart_last_h = newH;
1282 + }
1283 + }
1284 + };
1285 +
1286 + // process end event
1287 + document.onmouseup =
1288 + document.ontouchend =
1289 + this.element_legend_childs.resize_handler.onmouseup =
1290 + this.element_legend_childs.resize_handler.ontouchend =
1291 + function(e) {
1292 + // remove all the hooks
1293 + document.onmouseup =
1294 + document.onmousemove =
1295 + document.ontouchmove =
1296 + document.ontouchend =
1297 + that.element_legend_childs.resize_handler.onmousemove =
1298 + that.element_legend_childs.resize_handler.ontouchmove =
1299 + that.element_legend_childs.resize_handler.onmouseout =
1300 + that.element_legend_childs.resize_handler.onmouseup =
1301 + that.element_legend_childs.resize_handler.ontouchend =
1302 + null;
1303 +
1304 + // allow auto-refreshes
1305 + NETDATA.options.auto_refresher_stop_until = 0;
1306 + };
1307 + }
1308 + }
1309
1345 - var min_duration = Math.round((this.chartWidth() / 30 * this.chart.update_every * 1000));
1310
1347 - if(this.debug === true)
1348 - this.log('requested duration of ' + ((before - after) / 1000).toString() + ' (' + after + ' - ' + before + '), minimum ' + min_duration / 1000);
1311 + var noDataToShow = function() {
1312 + this.legendUpdateDOM();
1313 + that.tm.last_autorefreshed = new Date().getTime();
1314 + that.data_update_every = 30 * 1000;
1315 + }
1316
1350 - if((before - after) < min_duration) return false;
1317 + // ============================================================================================================
1318 + // PUBLIC FUNCTIONS
1319
1352 - var current_duration = this.data_before - this.data_after;
1353 - var wanted_duration = before - after;
1354 - var tolerance = this.data_update_every * 2;
1355 - var movement = Math.abs(before - this.data_before);
1320 + this.setMode = function(m) {
1321 + if(this.current !== null && this.current.name === m) return;
1322
1357 - if(this.debug === true)
1358 - this.log('current duration: ' + current_duration / 1000 + ', wanted duration: ' + wanted_duration / 1000 + ', movement: ' + movement / 1000 + ', tolerance: ' + tolerance / 1000);
1323 + if(m === 'auto')
1324 + this.current = this.auto;
1325 + else if(m === 'pan')
1326 + this.current = this.pan;
1327 + else if(m === 'zoom')
1328 + this.current = this.zoom;
1329 + else
1330 + this.current = this.auto;
1331
1360 - if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) {
1361 - if(this.debug === true)
1362 - this.log('IGNORED');
1332 + this.current.force_update_at = 0;
1333 + this.current.force_before_ms = null;
1334 + this.current.force_after_ms = null;
1335
1364 - return false;
1336 + this.tm.last_mode_switch = new Date().getTime();
1337 }
1338
1367 - if(this.current.name === 'auto') {
1368 - this.setMode('pan');
1339 + // ----------------------------------------------------------------------------------------------------------------
1340 + // global selection sync
1341
1370 - if(this.debug === true)
1371 - this.log('updateChartPanOrZoom(): caller did not set proper mode');
1342 + // prevent to global selection sync for some time
1343 + this.globalSelectionSyncDelay = function(ms) {
1344 + if(NETDATA.options.current.sync_selection === false)
1345 + return;
1346 +
1347 + if(typeof ms === 'number')
1348 + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
1349 + else
1350 + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
1351 }
1352
1374 - this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1375 - this.current.force_after_ms = after;
1376 - this.current.force_before_ms = before;
1377 - NETDATA.globalPanAndZoom.setMaster(this, after, before);
1378 - return true;
1379 - }
1353 + // can we globally apply selection sync?
1354 + this.globalSelectionSyncAbility = function() {
1355 + if(NETDATA.options.current.sync_selection === false)
1356 + return false;
1357
1381 - chartState.prototype.legendFormatValue = function(value) {
1382 - if(value === null || value === 'undefined') return '-';
1383 - if(typeof value !== 'number') return value;
1358 + if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
1359 + return false;
1360
1385 - var abs = Math.abs(value);
1386 - if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
1387 - if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString();
1388 - return (Math.round(value * 10000) / 10000).toLocaleString();
1389 - }
1361 + return true;
1362 + }
1363 +
1364 + this.globalSelectionSyncIsMaster = function() {
1365 + if(NETDATA.globalSelectionSync.state === this)
1366 + return true;
1367 + else
1368 + return false;
1369 + }
1370
1391 - chartState.prototype.legendSetLabelValue = function(label, value) {
1392 - var series = this.element_legend_childs.series[label];
1393 - if(typeof series === 'undefined') return;
1394 - if(series.value === null && series.user === null) return;
1371 + // this chart is the master of the global selection sync
1372 + this.globalSelectionSyncBeMaster = function() {
1373 + // am I the master?
1374 + if(this.globalSelectionSyncIsMaster()) {
1375 + if(this.debug === true)
1376 + this.log('sync: I am the master already.');
1377
1396 - // if the value has not changed, skip DOM update
1397 - //if(series.last === value) return;
1378 + return;
1379 + }
1380
1399 - var s, r;
1400 - if(typeof value === 'number') {
1401 - var v = Math.abs(value);
1402 - s = r = this.legendFormatValue(value);
1381 + if(NETDATA.globalSelectionSync.state) {
1382 + if(this.debug === true)
1383 + this.log('sync: I am not the sync master. Resetting global sync.');
1384
1404 - if(typeof series.last === 'number') {
1405 - if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1406 - else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1407 - else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1385 + this.globalSelectionSyncStop();
1386 }
1409 - else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1410 - series.last = v;
1411 - }
1412 - else {
1413 - s = r = value;
1414 - series.last = value;
1415 - }
1387
1417 - if(series.value !== null) series.value.innerHTML = s;
1418 - if(series.user !== null) series.user.innerHTML = r;
1419 - }
1388 + // become the master
1389 + if(this.debug === true)
1390 + this.log('sync: becoming sync master.');
1391
1421 - chartState.prototype.legendSetDate = function(ms) {
1422 - if(typeof ms !== 'number') {
1423 - this.legendShowUndefined();
1424 - return;
1425 - }
1392 + this.selected = true;
1393 + NETDATA.globalSelectionSync.state = this;
1394
1427 - var d = new Date(ms);
1395 + // find the all slaves
1396 + var targets = NETDATA.options.targets;
1397 + var len = targets.length;
1398 + while(len--) {
1399 + st = targets[len];
1400
1429 - if(this.element_legend_childs.title_date)
1430 - this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
1401 + if(st === this) {
1402 + if(this.debug === true)
1403 + st.log('sync: not adding me to sync');
1404 + }
1405 + else if(st.globalSelectionSyncIsEligible()) {
1406 + if(this.debug === true)
1407 + st.log('sync: adding to sync as slave');
1408
1432 - if(this.element_legend_childs.title_time)
1433 - this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
1409 + st.globalSelectionSyncBeSlave();
1410 + }
1411 + }
1412
1435 - if(this.element_legend_childs.title_units)
1436 - this.element_legend_childs.title_units.innerHTML = this.chart.units;
1437 - }
1413 + // this.globalSelectionSyncDelay(100);
1414 + }
1415
1439 - chartState.prototype.legendShowUndefined = function() {
1440 - if(this.element_legend_childs.title_date)
1441 - this.element_legend_childs.title_date.innerHTML = '&nbsp;';
1416 + // can the chart participate to the global selection sync as a slave?
1417 + this.globalSelectionSyncIsEligible = function() {
1418 + if(this.enabled === true
1419 + && this.library !== null
1420 + && typeof this.library.setSelection === 'function'
1421 + && this.isVisible() === true
1422 + && this.chart_created === true)
1423 + return true;
1424
1443 - if(this.element_legend_childs.title_time)
1444 - this.element_legend_childs.title_time.innerHTML = this.chart.name;
1425 + return false;
1426 + }
1427
1446 - if(this.element_legend_childs.title_units)
1447 - this.element_legend_childs.title_units.innerHTML = '&nbsp;';
1428 + // this chart becomes a slave of the global selection sync
1429 + this.globalSelectionSyncBeSlave = function() {
1430 + if(NETDATA.globalSelectionSync.state !== this)
1431 + NETDATA.globalSelectionSync.slaves.push(this);
1432 + }
1433
1449 - if(this.data && this.element_legend_childs.series !== null) {
1450 - var labels = this.data.dimension_names;
1451 - var i = labels.length;
1452 - while(i--) {
1453 - var label = labels[i];
1434 + // sync all the visible charts to the given time
1435 + // this is to be called from the chart libraries
1436 + this.globalSelectionSync = function(t) {
1437 + if(this.globalSelectionSyncAbility() === false) {
1438 + if(this.debug === true)
1439 + this.log('sync: cannot sync (yet?).');
1440
1455 - if(typeof label === 'undefined') continue;
1456 - if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1457 - this.legendSetLabelValue(label, null);
1441 + return;
1442 }
1459 - }
1460 - }
1443
1462 - chartState.prototype.legendShowLatestValues = function() {
1463 - if(this.chart === null) return;
1464 - if(this.selected) return;
1465 -
1466 - if(this.data === null || this.element_legend_childs.series === null) {
1467 - this.legendShowUndefined();
1468 - return;
1469 - }
1470 -
1471 - var show_undefined = true;
1472 - if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every)
1473 - show_undefined = false;
1474 -
1475 - if(show_undefined) {
1476 - this.legendShowUndefined();
1477 - return;
1478 - }
1444 + if(this.globalSelectionSyncIsMaster() === false) {
1445 + if(this.debug === true)
1446 + this.log('sync: trying to be sync master.');
1447
1480 - this.legendSetDate(this.data.before * 1000);
1448 + this.globalSelectionSyncBeMaster();
1449
1482 - var labels = this.data.dimension_names;
1483 - var i = labels.length;
1484 - while(i--) {
1485 - var label = labels[i];
1450 + if(this.globalSelectionSyncAbility() === false) {
1451 + if(this.debug === true)
1452 + this.log('sync: cannot sync (yet?).');
1453
1487 - if(typeof label === 'undefined') continue;
1488 - if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1454 + return;
1455 + }
1456 + }
1457
1490 - if(show_undefined)
1491 - this.legendSetLabelValue(label, null);
1492 - else
1493 - this.legendSetLabelValue(label, this.data.view_latest_values[i]);
1458 + NETDATA.globalSelectionSync.last_t = t;
1459 + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1460 + st.setSelection(t);
1461 + });
1462 }
1495 - }
1463
1497 - chartState.prototype.legendReset = function() {
1498 - this.legendShowLatestValues();
1499 - }
1464 + // stop syncing all charts to the given time
1465 + this.globalSelectionSyncStop = function() {
1466 + if(NETDATA.globalSelectionSync.slaves.length) {
1467 + if(this.debug === true)
1468 + this.log('sync: cleaning up...');
1469
1501 - // this should be called just ONCE per dimension per chart
1502 - chartState.prototype._chartDimensionColor = function(label) {
1503 - if(this.colors === null) this.chartColors();
1470 + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
1471 + if(st === that) {
1472 + if(that.debug === true)
1473 + st.log('sync: not adding me to sync stop');
1474 + }
1475 + else {
1476 + if(that.debug === true)
1477 + st.log('sync: removed slave from sync');
1478
1505 - if(typeof this.colors_assigned[label] === 'undefined') {
1506 - if(this.colors_available.length === 0) {
1507 - for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
1508 - this.colors_available.push(NETDATA.colors[i]);
1509 - }
1479 + st.clearSelection();
1480 + }
1481 + });
1482
1511 - this.colors_assigned[label] = this.colors_available.shift();
1483 + NETDATA.globalSelectionSync.last_t = 0;
1484 + NETDATA.globalSelectionSync.slaves = [];
1485 + NETDATA.globalSelectionSync.state = null;
1486 + }
1487
1513 - if(this.debug === true)
1514 - this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
1515 - }
1516 - else {
1517 - if(this.debug === true)
1518 - this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
1488 + // since we are the sync master, we should not call this.clearSelection()
1489 + // dygraphs is taking care of visualizing our selection.
1490 + this.selected = false;
1491 + this.clearSelection();
1492 }
1493
1521 - this.colors.push(this.colors_assigned[label]);
1522 - return this.colors_assigned[label];
1523 - }
1494 + this.setSelection = function(t) {
1495 + if(typeof this.library.setSelection === 'function') {
1496 + if(this.library.setSelection(this, t) === true)
1497 + this.selected = true;
1498 + else
1499 + this.selected = false;
1500 + }
1501 + else this.selected = true;
1502
1525 - chartState.prototype.chartColors = function() {
1526 - if(this.colors !== null) return this.colors;
1503 + if(this.selected === true && this.debug === true)
1504 + this.log('selection set to ' + t.toString());
1505
1528 - this.colors = new Array();
1529 - this.colors_available = new Array();
1530 - // this.colors_assigned = {};
1506 + return this.selected;
1507 + }
1508
1532 - var c = $(this.element).data('colors');
1533 - if(typeof c !== 'undefined' && c !== null) {
1534 - if(typeof c !== 'string') {
1535 - this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
1536 - }
1537 - else {
1538 - c = c.split(' ');
1539 - for(var i = 0, len = c.length; i < len ; i++)
1540 - this.colors_available.push(c[i]);
1509 + this.clearSelection = function() {
1510 + if(this.selected === true) {
1511 + if(typeof this.library.clearSelection === 'function') {
1512 + if(this.library.clearSelection(this) === true)
1513 + this.selected = false;
1514 + else
1515 + this.selected = true;
1516 + }
1517 + else this.selected = false;
1518 +
1519 + if(this.selected === false && this.debug === true)
1520 + this.log('selection cleared');
1521 }
1522 +
1523 + this.legendReset();
1524 + return this.selected;
1525 }
1526
1544 - // push all the standard colors too
1545 - for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
1546 - this.colors_available.push(NETDATA.colors[i]);
1527 + // find if a timestamp (ms) is shown in the current chart
1528 + this.timeIsVisible = function(t) {
1529 + if(t >= this.data_after && t <= this.data_before)
1530 + return true;
1531 + return false;
1532 + },
1533
1548 - return this.colors;
1549 - }
1534 + this.calculateRowForTime = function(t) {
1535 + if(this.timeIsVisible(t) === false) return -1;
1536 + return Math.floor((t - this.data_after) / this.data_update_every);
1537 + }
1538
1551 - chartState.prototype.legendUpdateDOM = function() {
1552 - var needed = false;
1539 + // ----------------------------------------------------------------------------------------------------------------
1540
1554 - // check that the legend DOM is up to date for the downloaded dimensions
1555 - if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
1556 - // this.log('the legend does not have any series - requesting legend update');
1557 - needed = true;
1558 - }
1559 - else if(this.data === null) {
1560 - // this.log('the chart does not have any data - requesting legend update');
1561 - needed = true;
1541 + // console logging
1542 + this.log = function(msg) {
1543 + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
1544 }
1563 - else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
1564 - needed = true;
1565 - }
1566 - else {
1567 - var labels = this.data.dimension_names.toString();
1568 - if(labels !== this.element_legend_childs.series.labels_key) {
1569 - needed = true;
1545
1546 + this.pauseChart = function() {
1547 + if(this.paused === false) {
1548 if(this.debug === true)
1572 - this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
1549 + this.log('paused');
1550 +
1551 + this.paused = true;
1552 }
1553 }
1554
1576 - if(needed === false) {
1577 - // make sure colors available
1578 - this.chartColors();
1579 -
1580 - // do we have to update the current values?
1581 - // we do this, only when the visible chart is current
1582 - if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) {
1555 + this.unpauseChart = function() {
1556 + if(this.paused) {
1557 if(this.debug === true)
1584 - this.log('chart in running... updating values on legend...');
1558 + this.log('unpaused');
1559
1586 - //var labels = this.data.dimension_names;
1587 - //var i = labels.length;
1588 - //while(i--)
1589 - // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
1560 + this.paused = false;
1561 }
1591 - return;
1562 }
1593 - if(this.colors === null) {
1594 - // this is the first time we update the chart
1595 - // let's assign colors to all dimensions
1596 - if(this.library.track_colors() === true)
1597 - for(var dim in this.chart.dimensions)
1598 - this._chartDimensionColor(this.chart.dimensions[dim].name);
1599 - }
1600 - // we will re-generate the colors for the chart
1601 - this.colors = null;
1563
1603 - if(this.debug === true)
1604 - this.log('updating Legend DOM');
1564 + this.resetChart = function() {
1565 + if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible())
1566 + NETDATA.globalPanAndZoom.clearMaster();
1567
1606 - // mark all dimensions as invalid
1607 - this.dimensions_visibility.invalidateAll();
1608 -
1609 - var self = $(this.element);
1610 - var genLabel = function(state, parent, name, count) {
1611 - var color = state._chartDimensionColor(name);
1568 + this.tm.pan_and_zoom_seq = 0;
1569
1613 - var user_element = null;
1614 - var user_id = self.data('show-value-of-' + name + '-at') || null;
1615 - if(user_id !== null) {
1616 - user_element = document.getElementById(user_id) || null;
1617 - if(user_element === null)
1618 - me.log('Cannot find element with id: ' + user_id);
1619 - }
1570 + this.clearSelection();
1571
1621 - state.element_legend_childs.series[name] = {
1622 - name: document.createElement('span'),
1623 - value: document.createElement('span'),
1624 - user: user_element,
1625 - last: null
1626 - };
1572 + this.setMode('auto');
1573 + this.current.force_update_at = 0;
1574 + this.current.force_before_ms = null;
1575 + this.current.force_after_ms = null;
1576 + this.tm.last_autorefreshed = 0;
1577 + this.paused = false;
1578 + this.selected = false;
1579 + this.enabled = true;
1580 + this.debug = false;
1581
1628 - var label = state.element_legend_childs.series[name];
1582 + // do not update the chart here
1583 + // or the chart will flip-flop when it is the master
1584 + // of a selection sync and another chart becomes
1585 + // the new master
1586 + if(NETDATA.options.current.sync_pan_and_zoom === false && this.isVisible() === true)
1587 + this.updateChart();
1588 + }
1589
1630 - // create the dimension visibility tracking for this label
1631 - var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
1590 + this.updateChartPanOrZoom = function(after, before) {
1591 + if(before < after) return false;
1592
1633 - var rgb = NETDATA.colorHex2Rgb(color);
1634 - label.name.innerHTML = '<table class="netdata-legend-name-table-'
1635 - + state.chart.chart_type
1636 - + '" style="background-color: '
1637 - + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
1638 - + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
1593 + var min_duration = Math.round((this.chartWidth() / 30 * this.chart.update_every * 1000));
1594
1640 - var text = document.createTextNode(' ' + name);
1641 - label.name.appendChild(text);
1595 + if(this.debug === true)
1596 + this.log('requested duration of ' + ((before - after) / 1000).toString() + ' (' + after + ' - ' + before + '), minimum ' + min_duration / 1000);
1597
1643 - if(count > 0)
1644 - parent.appendChild(document.createElement('br'));
1598 + if((before - after) < min_duration) return false;
1599
1646 - parent.appendChild(label.name);
1647 - parent.appendChild(label.value);
1648 - };
1600 + var current_duration = this.data_before - this.data_after;
1601 + var wanted_duration = before - after;
1602 + var tolerance = this.data_update_every * 2;
1603 + var movement = Math.abs(before - this.data_before);
1604
1650 - var content = document.createElement('div');
1651 -
1652 - if(this.hasLegend()) {
1653 - this.element_legend_childs = {
1654 - content: content,
1655 - resize_handler: document.createElement('div'),
1656 - title_date: document.createElement('span'),
1657 - title_time: document.createElement('span'),
1658 - title_units: document.createElement('span'),
1659 - nano: document.createElement('div'),
1660 - nano_options: {
1661 - paneClass: 'netdata-legend-series-pane',
1662 - sliderClass: 'netdata-legend-series-slider',
1663 - contentClass: 'netdata-legend-series-content',
1664 - enabledClass: '__enabled',
1665 - flashedClass: '__flashed',
1666 - activeClass: '__active',
1667 - tabIndex: -1,
1668 - alwaysVisible: true,
1669 - sliderMinHeight: 10
1670 - },
1671 - series: {}
1672 - };
1605 + if(this.debug === true)
1606 + this.log('current duration: ' + current_duration / 1000 + ', wanted duration: ' + wanted_duration / 1000 + ', movement: ' + movement / 1000 + ', tolerance: ' + tolerance / 1000);
1607
1674 - this.element_legend.innerHTML = '';
1608 + if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) {
1609 + if(this.debug === true)
1610 + this.log('IGNORED');
1611
1676 - this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
1677 - this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
1678 - this.element.appendChild(this.element_legend_childs.resize_handler);
1679 - var self2 = this;
1612 + return false;
1613 + }
1614
1681 - // mousedown event
1682 - this.element_legend_childs.resize_handler.onmousedown =
1683 - function(e) {
1684 - self2.resizeHandler(e);
1685 - };
1615 + if(this.current.name === 'auto') {
1616 + this.setMode('pan');
1617
1687 - // touchstart event
1688 - this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
1689 - self2.resizeHandler(e);
1690 - }, false);
1618 + if(this.debug === true)
1619 + this.log('updateChartPanOrZoom(): caller did not set proper mode');
1620 + }
1621
1692 - this.element_legend_childs.title_date.className += " netdata-legend-title-date";
1693 - this.element_legend.appendChild(this.element_legend_childs.title_date);
1622 + this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
1623 + this.current.force_after_ms = after;
1624 + this.current.force_before_ms = before;
1625 + NETDATA.globalPanAndZoom.setMaster(this, after, before);
1626 + return true;
1627 + }
1628
1695 - this.element_legend.appendChild(document.createElement('br'));
1629 + this.legendFormatValue = function(value) {
1630 + if(value === null || value === 'undefined') return '-';
1631 + if(typeof value !== 'number') return value;
1632
1697 - this.element_legend_childs.title_time.className += " netdata-legend-title-time";
1698 - this.element_legend.appendChild(this.element_legend_childs.title_time);
1633 + var abs = Math.abs(value);
1634 + if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString();
1635 + if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString();
1636 + return (Math.round(value * 10000) / 10000).toLocaleString();
1637 + }
1638
1700 - this.element_legend.appendChild(document.createElement('br'));
1639 + this.legendSetLabelValue = function(label, value) {
1640 + var series = this.element_legend_childs.series[label];
1641 + if(typeof series === 'undefined') return;
1642 + if(series.value === null && series.user === null) return;
1643
1702 - this.element_legend_childs.title_units.className += " netdata-legend-title-units";
1703 - this.element_legend.appendChild(this.element_legend_childs.title_units);
1644 + // if the value has not changed, skip DOM update
1645 + //if(series.last === value) return;
1646
1705 - this.element_legend.appendChild(document.createElement('br'));
1647 + var s, r;
1648 + if(typeof value === 'number') {
1649 + var v = Math.abs(value);
1650 + s = r = this.legendFormatValue(value);
1651
1707 - this.element_legend_childs.nano.className = 'netdata-legend-series';
1708 - this.element_legend.appendChild(this.element_legend_childs.nano);
1652 + if(typeof series.last === 'number') {
1653 + if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1654 + else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1655 + else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1656 + }
1657 + else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
1658 + series.last = v;
1659 + }
1660 + else {
1661 + s = r = value;
1662 + series.last = value;
1663 + }
1664
1710 - content.className = 'netdata-legend-series-content';
1711 - this.element_legend_childs.nano.appendChild(content);
1712 - }
1713 - else {
1714 - this.element_legend_childs = {
1715 - content: content,
1716 - resize_handler: null,
1717 - title_date: null,
1718 - title_time: null,
1719 - title_units: null,
1720 - nano: null,
1721 - nano_options: null,
1722 - series: {}
1723 - };
1665 + if(series.value !== null) series.value.innerHTML = s;
1666 + if(series.user !== null) series.user.innerHTML = r;
1667 }
1668
1726 - if(this.data) {
1727 - this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
1728 - if(this.debug === true)
1729 - this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
1730 -
1731 - for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
1732 - genLabel(this, content, this.data.dimension_names[i], i);
1733 - }
1734 - }
1735 - else {
1736 - var tmp = new Array();
1737 - for(var dim in this.chart.dimensions) {
1738 - tmp.push(this.chart.dimensions[dim].name);
1739 - genLabel(this, content, this.chart.dimensions[dim].name, i);
1669 + this.legendSetDate = function(ms) {
1670 + if(typeof ms !== 'number') {
1671 + this.legendShowUndefined();
1672 + return;
1673 }
1741 - this.element_legend_childs.series.labels_key = tmp.toString();
1742 - if(this.debug === true)
1743 - this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
1744 - }
1674
1746 - // create a hidden div to be used for hidding
1747 - // the original legend of the chart library
1748 - var el = document.createElement('div');
1749 - this.element_legend.appendChild(el);
1750 - el.style.display = 'none';
1675 + var d = new Date(ms);
1676
1752 - this.element_legend_childs.hidden = document.createElement('div');
1753 - el.appendChild(this.element_legend_childs.hidden);
1677 + if(this.element_legend_childs.title_date)
1678 + this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
1679
1755 - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
1756 - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
1680 + if(this.element_legend_childs.title_time)
1681 + this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
1682
1758 - this.legendShowLatestValues();
1759 - }
1683 + if(this.element_legend_childs.title_units)
1684 + this.element_legend_childs.title_units.innerHTML = this.chart.units;
1685 + }
1686
1761 - chartState.prototype.createChartDOM = function() {
1762 - if(this.debug === true)
1763 - this.log('creating DOM');
1764 -
1765 - this.element_chart_id = this.library_name + '-' + this.uuid + '-chart';
1766 - this.element_chart = document.createElement('div');
1767 - this.element_chart.className += ' netdata-chart' + (this.hasLegend()?'-with-legend-right':'').toString();
1768 - this.element_chart.className += ' netdata-' + this.library_name + '-chart' + (this.hasLegend()?'-with-legend-right':'').toString();
1769 - this.element_chart.id = this.element_chart_id;
1770 - $(this.element_chart).data('netdata-state-object', this);
1771 - this.element.appendChild(this.element_chart);
1772 -
1773 - this.element_legend_id = this.library_name + '-' + this.uuid + '-legend';
1774 - this.element_legend = document.createElement('div');
1775 - this.element_legend.className += ' netdata-chart-legend';
1776 - this.element_legend.className += ' netdata-' + this.library_name + '-legend';
1777 - this.element_legend.id = this.element_legend_id;
1778 - $(this.element_legend).data('netdata-state-object', this);
1779 -
1780 - if(this.hasLegend() === false)
1781 - this.element_legend.style.display = 'none';
1782 - else
1783 - this.element.appendChild(this.element_legend);
1687 + this.legendShowUndefined = function() {
1688 + if(this.element_legend_childs.title_date)
1689 + this.element_legend_childs.title_date.innerHTML = '&nbsp;';
1690
1785 - this.element_legend_childs.series = null;
1786 - this.legendUpdateDOM();
1691 + if(this.element_legend_childs.title_time)
1692 + this.element_legend_childs.title_time.innerHTML = this.chart.name;
1693
1788 - // in case the user has an active global selection sync in place
1789 - // reset it
1790 - this.globalSelectionSyncStop();
1791 - this.dom_created = true;
1792 - }
1694 + if(this.element_legend_childs.title_units)
1695 + this.element_legend_childs.title_units.innerHTML = '&nbsp;';
1696
1794 - chartState.prototype.hasLegend = function() {
1795 - if(typeof this.___hasLegendCache___ !== 'undefined')
1796 - return this.___hasLegendCache___;
1697 + if(this.data && this.element_legend_childs.series !== null) {
1698 + var labels = this.data.dimension_names;
1699 + var i = labels.length;
1700 + while(i--) {
1701 + var label = labels[i];
1702
1798 - var leg = false;
1799 - if(this.library && this.library.legend(this) === 'right-side') {
1800 - var legend = $(this.element).data('legend') || 'yes';
1801 - if(legend === 'yes') leg = true;
1703 + if(typeof label === 'undefined') continue;
1704 + if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1705 + this.legendSetLabelValue(label, null);
1706 + }
1707 + }
1708 }
1709
1804 - this.___hasLegendCache___ = leg;
1805 - return leg;
1806 - }
1710 + this.legendShowLatestValues = function() {
1711 + if(this.chart === null) return;
1712 + if(this.selected) return;
1713
1808 - chartState.prototype.legendWidth = function() {
1809 - return (this.hasLegend())?140:0;
1810 - }
1714 + if(this.data === null || this.element_legend_childs.series === null) {
1715 + this.legendShowUndefined();
1716 + return;
1717 + }
1718
1812 - chartState.prototype.legendHeight = function() {
1813 - return $(this.element).height();
1814 - }
1719 + var show_undefined = true;
1720 + if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every)
1721 + show_undefined = false;
1722
1816 - chartState.prototype.chartWidth = function() {
1817 - return $(this.element).width() - this.legendWidth();
1818 - }
1723 + if(show_undefined) {
1724 + this.legendShowUndefined();
1725 + return;
1726 + }
1727
1820 - chartState.prototype.chartHeight = function() {
1821 - return $(this.element).height();
1822 - }
1728 + this.legendSetDate(this.data.before * 1000);
1729
1824 - chartState.prototype.chartPixelsPerPoint = function() {
1825 - // force an options provided detail
1826 - var px = this.pixels_per_point;
1730 + var labels = this.data.dimension_names;
1731 + var i = labels.length;
1732 + while(i--) {
1733 + var label = labels[i];
1734
1828 - if(this.library && px < this.library.pixels_per_point(this))
1829 - px = this.library.pixels_per_point(this);
1735 + if(typeof label === 'undefined') continue;
1736 + if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
1737
1831 - if(px < NETDATA.options.current.pixels_per_point)
1832 - px = NETDATA.options.current.pixels_per_point;
1738 + if(show_undefined)
1739 + this.legendSetLabelValue(label, null);
1740 + else
1741 + this.legendSetLabelValue(label, this.data.view_latest_values[i]);
1742 + }
1743 + }
1744
1834 - return px;
1835 - }
1745 + this.legendReset = function() {
1746 + this.legendShowLatestValues();
1747 + }
1748
1837 - chartState.prototype.needsRecreation = function() {
1838 - return (
1839 - this.dom_created === true
1840 - && this.chart_created === true
1841 - && this.library
1842 - && this.library.autoresize() === false
1843 - && this.tm.last_resized < NETDATA.options.last_resized
1844 - );
1845 - }
1749 + // this should be called just ONCE per dimension per chart
1750 + this._chartDimensionColor = function(label) {
1751 + if(this.colors === null) this.chartColors();
1752
1847 - chartState.prototype.resizeChart = function() {
1848 - if(this.needsRecreation()) {
1849 - if(this.debug === true)
1850 - this.log('forcing re-generation due to window resize.');
1753 + if(typeof this.colors_assigned[label] === 'undefined') {
1754 + if(this.colors_available.length === 0) {
1755 + for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
1756 + this.colors_available.push(NETDATA.colors[i]);
1757 + }
1758
1852 - this.destroyChart();
1853 - }
1759 + this.colors_assigned[label] = this.colors_available.shift();
1760
1855 - this.tm.last_resized = new Date().getTime();
1856 - }
1761 + if(this.debug === true)
1762 + this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
1763 + }
1764 + else {
1765 + if(this.debug === true)
1766 + this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
1767 + }
1768
1858 - chartState.prototype.chartURL = function() {
1859 - var before;
1860 - var after;
1861 - if(NETDATA.globalPanAndZoom.isActive()) {
1862 - after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
1863 - before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
1864 - this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
1769 + this.colors.push(this.colors_assigned[label]);
1770 + return this.colors_assigned[label];
1771 }
1866 - else {
1867 - before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before;
1868 - after = this.current.force_after_ms !== null ? Math.round(this.current.force_after_ms / 1000) : this.after;
1869 - this.tm.pan_and_zoom_seq = 0;
1772 +
1773 + this.chartColors = function() {
1774 + if(this.colors !== null) return this.colors;
1775 +
1776 + this.colors = new Array();
1777 + this.colors_available = new Array();
1778 + // this.colors_assigned = {};
1779 +
1780 + var c = $(this.element).data('colors');
1781 + if(typeof c !== 'undefined' && c !== null) {
1782 + if(typeof c !== 'string') {
1783 + this.log('invalid color given: ' + c + ' (give a space separated list of colors)');
1784 + }
1785 + else {
1786 + c = c.split(' ');
1787 + for(var i = 0, len = c.length; i < len ; i++)
1788 + this.colors_available.push(c[i]);
1789 + }
1790 + }
1791 +
1792 + // push all the standard colors too
1793 + for(var i = 0, len = NETDATA.colors.length; i < len ; i++)
1794 + this.colors_available.push(NETDATA.colors[i]);
1795 +
1796 + return this.colors;
1797 }
1798
1872 - this.current.requested_after_ms = after * 1000;
1873 - this.current.requested_before_ms = before * 1000;
1799 + this.legendUpdateDOM = function() {
1800 + var needed = false;
1801
1875 - this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
1802 + // check that the legend DOM is up to date for the downloaded dimensions
1803 + if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
1804 + // this.log('the legend does not have any series - requesting legend update');
1805 + needed = true;
1806 + }
1807 + else if(this.data === null) {
1808 + // this.log('the chart does not have any data - requesting legend update');
1809 + needed = true;
1810 + }
1811 + else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
1812 + needed = true;
1813 + }
1814 + else {
1815 + var labels = this.data.dimension_names.toString();
1816 + if(labels !== this.element_legend_childs.series.labels_key) {
1817 + needed = true;
1818
1877 - // build the data URL
1878 - this.data_url = this.chart.data_url;
1879 - this.data_url += "&format=" + this.library.format();
1880 - this.data_url += "&points=" + this.data_points.toString();
1881 - this.data_url += "&group=" + this.method;
1882 - this.data_url += "&options=" + this.library.options();
1883 - this.data_url += '|jsonwrap';
1819 + if(this.debug === true)
1820 + this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
1821 + }
1822 + }
1823
1885 - if(NETDATA.options.current.eliminate_zero_dimensions === true)
1886 - this.data_url += '|nonzero';
1824 + if(needed === false) {
1825 + // make sure colors available
1826 + this.chartColors();
1827
1888 - if(after)
1889 - this.data_url += "&after=" + after.toString();
1828 + // do we have to update the current values?
1829 + // we do this, only when the visible chart is current
1830 + if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) {
1831 + if(this.debug === true)
1832 + this.log('chart in running... updating values on legend...');
1833
1891 - if(before)
1892 - this.data_url += "&before=" + before.toString();
1834 + //var labels = this.data.dimension_names;
1835 + //var i = labels.length;
1836 + //while(i--)
1837 + // this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
1838 + }
1839 + return;
1840 + }
1841 + if(this.colors === null) {
1842 + // this is the first time we update the chart
1843 + // let's assign colors to all dimensions
1844 + if(this.library.track_colors() === true)
1845 + for(var dim in this.chart.dimensions)
1846 + this._chartDimensionColor(this.chart.dimensions[dim].name);
1847 + }
1848 + // we will re-generate the colors for the chart
1849 + this.colors = null;
1850
1894 - if(this.dimensions)
1895 - this.data_url += "&dimensions=" + this.dimensions;
1851 + if(this.debug === true)
1852 + this.log('updating Legend DOM');
1853
1897 - if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
1898 - this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
1899 - }
1854 + // mark all dimensions as invalid
1855 + this.dimensions_visibility.invalidateAll();
1856
1901 - chartState.prototype.redrawChart = function() {
1902 - if(this.data !== null)
1903 - this.updateChartWithData(this.data);
1904 - }
1857 + var genLabel = function(state, parent, name, count) {
1858 + var color = state._chartDimensionColor(name);
1859
1906 - chartState.prototype.updateChartWithData = function(data) {
1907 - if(this.debug === true)
1908 - this.log('got data from netdata server');
1860 + var user_element = null;
1861 + var user_id = self.data('show-value-of-' + name + '-at') || null;
1862 + if(user_id !== null) {
1863 + user_element = document.getElementById(user_id) || null;
1864 + if(user_element === null)
1865 + me.log('Cannot find element with id: ' + user_id);
1866 + }
1867
1910 - this.data = data;
1911 - this.updates_counter++;
1868 + state.element_legend_childs.series[name] = {
1869 + name: document.createElement('span'),
1870 + value: document.createElement('span'),
1871 + user: user_element,
1872 + last: null
1873 + };
1874
1913 - var started = new Date().getTime();
1914 - this.tm.last_updated = started;
1875 + var label = state.element_legend_childs.series[name];
1876
1916 - // if the result is JSON, find the latest update-every
1917 - if(typeof data === 'object') {
1918 - if(typeof data.view_update_every !== 'undefined')
1919 - this.data_update_every = data.view_update_every * 1000;
1877 + // create the dimension visibility tracking for this label
1878 + var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
1879
1921 - if(typeof data.after !== 'undefined')
1922 - this.data_after = data.after * 1000;
1880 + var rgb = NETDATA.colorHex2Rgb(color);
1881 + label.name.innerHTML = '<table class="netdata-legend-name-table-'
1882 + + state.chart.chart_type
1883 + + '" style="background-color: '
1884 + + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
1885 + + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
1886
1924 - if(typeof data.before !== 'undefined')
1925 - this.data_before = data.before * 1000;
1887 + var text = document.createTextNode(' ' + name);
1888 + label.name.appendChild(text);
1889
1927 - if(typeof data.first_entry !== 'undefined')
1928 - this.netdata_first = data.first_entry * 1000;
1890 + if(count > 0)
1891 + parent.appendChild(document.createElement('br'));
1892
1930 - if(typeof data.last_entry !== 'undefined')
1931 - this.netdata_last = data.last_entry * 1000;
1893 + parent.appendChild(label.name);
1894 + parent.appendChild(label.value);
1895 + };
1896
1933 - if(typeof data.points !== 'undefined')
1934 - this.data_points = data.points;
1897 + var content = document.createElement('div');
1898 +
1899 + if(this.hasLegend()) {
1900 + this.element_legend_childs = {
1901 + content: content,
1902 + resize_handler: document.createElement('div'),
1903 + title_date: document.createElement('span'),
1904 + title_time: document.createElement('span'),
1905 + title_units: document.createElement('span'),
1906 + nano: document.createElement('div'),
1907 + nano_options: {
1908 + paneClass: 'netdata-legend-series-pane',
1909 + sliderClass: 'netdata-legend-series-slider',
1910 + contentClass: 'netdata-legend-series-content',
1911 + enabledClass: '__enabled',
1912 + flashedClass: '__flashed',
1913 + activeClass: '__active',
1914 + tabIndex: -1,
1915 + alwaysVisible: true,
1916 + sliderMinHeight: 10
1917 + },
1918 + series: {}
1919 + };
1920
1936 - data.state = this;
1937 - }
1921 + this.element_legend.innerHTML = '';
1922
1939 - if(this.debug === true) {
1940 - this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
1923 + this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
1924 + this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
1925 + this.element.appendChild(this.element_legend_childs.resize_handler);
1926
1942 - if(this.current.force_after_ms)
1943 - this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
1944 - else
1945 - this.log('STATUS: forced: unset');
1927 + // mousedown event
1928 + this.element_legend_childs.resize_handler.onmousedown =
1929 + function(e) {
1930 + that.resizeHandler(e);
1931 + };
1932
1947 - this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString());
1948 - this.log('STATUS: rendered : ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
1949 - this.log('STATUS: points : ' + (this.data_points).toString());
1950 - }
1933 + // touchstart event
1934 + this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
1935 + that.resizeHandler(e);
1936 + }, false);
1937
1952 - if(this.data_points === 0) {
1953 - this.noData();
1954 - return;
1955 - }
1938 + this.element_legend_childs.title_date.className += " netdata-legend-title-date";
1939 + this.element_legend.appendChild(this.element_legend_childs.title_date);
1940
1957 - // this may force the chart to be re-created
1958 - this.resizeChart();
1941 + this.element_legend.appendChild(document.createElement('br'));
1942
1960 - if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
1961 - if(this.debug === true)
1962 - this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
1943 + this.element_legend_childs.title_time.className += " netdata-legend-title-time";
1944 + this.element_legend.appendChild(this.element_legend_childs.title_time);
1945
1964 - this.chart_created = false;
1965 - }
1946 + this.element_legend.appendChild(document.createElement('br'));
1947
1967 - if(this.chart_created === true
1968 - && this.dom_created === true
1969 - && typeof this.library.update === 'function') {
1948 + this.element_legend_childs.title_units.className += " netdata-legend-title-units";
1949 + this.element_legend.appendChild(this.element_legend_childs.title_units);
1950
1971 - if(this.debug === true)
1972 - this.log('updating chart...');
1951 + this.element_legend.appendChild(document.createElement('br'));
1952
1974 - // check and update the legend
1975 - this.legendUpdateDOM();
1953 + this.element_legend_childs.nano.className = 'netdata-legend-series';
1954 + this.element_legend.appendChild(this.element_legend_childs.nano);
1955
1977 - this.updates_since_last_creation++;
1978 - if(NETDATA.options.debug.chart_errors === true) {
1979 - this.library.update(this, data);
1956 + content.className = 'netdata-legend-series-content';
1957 + this.element_legend_childs.nano.appendChild(content);
1958 }
1959 else {
1982 - try {
1983 - this.library.update(this, data);
1984 - }
1985 - catch(err) {
1986 - this.error('chart failed to be updated as ' + this.library_name);
1987 - }
1960 + this.element_legend_childs = {
1961 + content: content,
1962 + resize_handler: null,
1963 + title_date: null,
1964 + title_time: null,
1965 + title_units: null,
1966 + nano: null,
1967 + nano_options: null,
1968 + series: {}
1969 + };
1970 }
1989 - }
1990 - else {
1991 - if(this.debug === true)
1992 - this.log('creating chart...');
1971
1994 - this.createChartDOM();
1995 - this.updates_since_last_creation = 0;
1972 + if(this.data) {
1973 + this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
1974 + if(this.debug === true)
1975 + this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
1976
1997 - if(NETDATA.options.debug.chart_errors === true) {
1998 - this.library.create(this, data);
1999 - this.chart_created = true;
1977 + for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
1978 + genLabel(this, content, this.data.dimension_names[i], i);
1979 + }
1980 }
1981 else {
2002 - try {
2003 - this.library.create(this, data);
2004 - this.chart_created = true;
2005 - }
2006 - catch(err) {
2007 - this.error('chart failed to be created as ' + this.library_name);
1982 + var tmp = new Array();
1983 + for(var dim in this.chart.dimensions) {
1984 + tmp.push(this.chart.dimensions[dim].name);
1985 + genLabel(this, content, this.chart.dimensions[dim].name, i);
1986 }
1987 + this.element_legend_childs.series.labels_key = tmp.toString();
1988 + if(this.debug === true)
1989 + this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
1990 }
2010 - }
2011 - // this.legendShowLatestValues();
2012 -
2013 - // update the performance counters
2014 - var now = new Date().getTime();
2015 -
2016 - // don't update last_autorefreshed if this chart is
2017 - // forced to be updated with global PanAndZoom
2018 - if(NETDATA.globalPanAndZoom.isActive())
2019 - this.tm.last_autorefreshed = 0;
2020 - else {
2021 - if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes)
2022 - this.tm.last_autorefreshed = Math.round(now / this.data_update_every) * this.data_update_every;
2023 - else
2024 - this.tm.last_autorefreshed = now;
2025 - }
1991
2027 - this.refresh_dt_ms = now - started;
2028 - NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
1992 + // create a hidden div to be used for hidding
1993 + // the original legend of the chart library
1994 + var el = document.createElement('div');
1995 + if(this.element_legend !== null)
1996 + this.element_legend.appendChild(el);
1997 + el.style.display = 'none';
1998
2030 - if(this.refresh_dt_element)
2031 - this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2032 - }
1999 + this.element_legend_childs.hidden = document.createElement('div');
2000 + el.appendChild(this.element_legend_childs.hidden);
2001
2034 - chartState.prototype.updateChart = function(callback) {
2035 - // due to late initialization of charts and libraries
2036 - // we need to check this too
2037 - if(this.enabled === false) {
2038 - if(this.debug === true)
2039 - this.log('I am not enabled');
2002 + if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2003 + $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2004
2041 - if(typeof callback === 'function') callback();
2042 - return false;
2005 + this.legendShowLatestValues();
2006 }
2007
2045 - if(this.chart === null) {
2046 - var self = this;
2047 - this.getChart(function() { self.updateChart(callback); });
2048 - return;
2049 - }
2008 + this.hasLegend = function() {
2009 + if(typeof this.___hasLegendCache___ !== 'undefined')
2010 + return this.___hasLegendCache___;
2011
2051 - if(this.library.initialized === false) {
2052 - if(this.library.enabled === true) {
2053 - var self = this;
2054 - this.library.initialize(function() { self.updateChart(callback); });
2055 - return;
2012 + var leg = false;
2013 + if(this.library && this.library.legend(this) === 'right-side') {
2014 + var legend = $(this.element).data('legend') || 'yes';
2015 + if(legend === 'yes') leg = true;
2016 }
2057 - else {
2058 - this.error('chart library "' + this.library_name + '" is not available.');
2059 - if(typeof callback === 'function') callback();
2060 - return false;
2061 - }
2062 - }
2063 -
2064 - this.clearSelection();
2065 - this.chartURL();
2066 - this.showLoading();
2067 -
2068 - if(this.debug === true)
2069 - this.log('updating from ' + this.data_url);
2070 -
2071 - var self = this;
2072 - this.xhr = $.ajax( {
2073 - url: this.data_url,
2074 - crossDomain: NETDATA.options.crossDomainAjax,
2075 - cache: false,
2076 - async: true
2077 - })
2078 - .success(function(data) {
2079 - self.hideLoading();
2017
2081 - if(self.debug === true)
2082 - self.log('data received. updating chart.');
2083 -
2084 - self.updateChartWithData(data);
2085 - })
2086 - .fail(function() {
2087 - self.hideLoading();
2088 - self.error('data download failed for url: ' + self.data_url);
2089 - })
2090 - .always(function() {
2091 - self.hideLoading();
2092 - if(typeof callback === 'function') callback();
2093 - });
2094 - }
2095 -
2096 - chartState.prototype.destroyChart = function() {
2097 - if(this.debug === true)
2098 - this.log('destroying chart');
2099 -
2100 - if(this.element_message !== null) {
2101 - this.element_message.innerHTML = '';
2102 - this.element_message = null;
2018 + this.___hasLegendCache___ = leg;
2019 + return leg;
2020 }
2021
2105 - if(this.element_loading !== null) {
2106 - this.element_loading.innerHTML = '';
2107 - this.element_loading = null;
2022 + this.legendWidth = function() {
2023 + return (this.hasLegend())?140:0;
2024 }
2025
2110 - if(this.element_legend !== null) {
2111 - this.element_legend.innerHTML = '';
2112 - this.element_legend = null;
2026 + this.legendHeight = function() {
2027 + return $(this.element).height();
2028 }
2029
2115 - if(this.element_chart !== null) {
2116 - this.element_chart.innerHTML = '';
2117 - this.element_chart = null;
2030 + this.chartWidth = function() {
2031 + return $(this.element).width() - this.legendWidth();
2032 }
2033
2120 - this.element_legend_childs = {
2121 - hidden: null,
2122 - title_date: null,
2123 - title_time: null,
2124 - title_units: null,
2125 - nano: null,
2126 - nano_options: null,
2127 - series: null
2128 - };
2129 -
2130 - this.element.innerHTML = '';
2131 - this.refresh_dt_element = null;
2132 -
2133 - this.dom_created = false;
2134 - this.chart_created = false;
2135 - this.paused = false;
2136 - this.selected = false;
2137 - this.updates_counter = 0;
2138 - this.updates_since_last_creation = 0;
2139 - this.tm.pan_and_zoom_seq = 0;
2140 - this.tm.last_resized = 0;
2141 - this.tm.last_visible_check = 0;
2142 - this.tm.last_hidden = 0;
2143 - this.tm.last_unhidden = 0;
2144 - this.tm.last_autorefreshed = 0;
2145 -
2146 - this.data = null;
2147 - this.data_points = 0;
2148 - this.data_after = 0;
2149 - this.data_before = 0;
2150 - this.data_update_every = 0;
2151 - this.netdata_first = 0;
2152 - this.netdata_last = 0;
2153 - if(this.current !== null) {
2154 - this.current.force_update_at = 0;
2155 - this.current.force_after_ms = null;
2156 - this.current.force_before_ms = null;
2157 - this.current.requested_after_ms = null;
2158 - this.current.requested_before_ms = null;
2034 + this.chartHeight = function() {
2035 + return $(this.element).height();
2036 }
2160 - this.init();
2161 - }
2037
2163 - chartState.prototype.unhideChart = function() {
2164 - if(typeof this.___isHidden___ !== 'undefined' && this.enabled === true) {
2165 - if(this.debug === true)
2166 - this.log('unhiding chart');
2038 + this.chartPixelsPerPoint = function() {
2039 + // force an options provided detail
2040 + var px = this.pixels_per_point;
2041
2168 - this.element_message.style.display = 'none';
2169 - if(this.element_chart !== null) this.element_chart.style.display = 'inline-block';
2170 - if(this.element_legend !== null) this.element_legend.style.display = 'inline-block';
2171 - if(this.element_loading !== null) this.element_loading.style.display = 'none';
2172 - this.___isHidden___ = undefined;
2173 - this.element_message.innerHTML = 'chart ' + this.id + ' is visible now';
2042 + if(this.library && px < this.library.pixels_per_point(this))
2043 + px = this.library.pixels_per_point(this);
2044
2175 - // refresh the scrolbar
2176 - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
2177 - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
2045 + if(px < NETDATA.options.current.pixels_per_point)
2046 + px = NETDATA.options.current.pixels_per_point;
2047
2179 - this.tm.last_unhidden = new Date().getTime();
2048 + return px;
2049 }
2181 - }
2050
2183 - chartState.prototype.hideChart = function() {
2184 - if(typeof this.___isHidden___ === 'undefined' && this.enabled === true) {
2185 - if(NETDATA.options.current.destroy_on_hide === true)
2186 - this.destroyChart();
2187 -
2188 - if(this.debug === true)
2189 - this.log('hiding chart');
2190 -
2191 - this.element_message.style.display = 'inline-block';
2192 - if(this.element_chart !== null) this.element_chart.style.display = 'none';
2193 - if(this.element_legend !== null) this.element_legend.style.display = 'none';
2194 - if(this.element_loading !== null) this.element_loading.style.display = 'none';
2195 - this.___isHidden___ = true;
2196 - this.___showsLoading___ = undefined;
2197 - this.element_message.innerHTML = 'chart ' + this.id + ' is hidden to speed up the browser';
2198 - this.tm.last_hidden = new Date().getTime();
2051 + this.needsRecreation = function() {
2052 + return (
2053 + this.chart_created === true
2054 + && this.library
2055 + && this.library.autoresize() === false
2056 + && this.tm.last_resized < NETDATA.options.last_resized
2057 + );
2058 }
2200 - }
2059
2202 - chartState.prototype.hideLoading = function() {
2203 - if(typeof this.___showsLoading___ !== 'undefined' && this.enabled === true) {
2204 - if(this.debug === true)
2205 - this.log('hide loading...');
2206 -
2207 - this.element_message.style.display = 'none';
2208 - if(this.element_chart !== null) this.element_chart.style.display = 'inline-block';
2209 - if(this.element_legend !== null) this.element_legend.style.display = 'inline-block';
2210 - if(this.element_loading !== null) this.element_loading.style.display = 'none';
2211 - this.___showsLoading___ = undefined;
2212 - this.element_loading.innerHTML = 'chart ' + this.id + ' finished loading!';
2213 - this.tm.last_unhidden = new Date().getTime();
2214 - }
2215 - }
2060 + this.chartURL = function() {
2061 + var before;
2062 + var after;
2063 + if(NETDATA.globalPanAndZoom.isActive()) {
2064 + after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
2065 + before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
2066 + this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
2067 + }
2068 + else {
2069 + before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before;
2070 + after = this.current.force_after_ms !== null ? Math.round(this.current.force_after_ms / 1000) : this.after;
2071 + this.tm.pan_and_zoom_seq = 0;
2072 + }
2073
2217 - chartState.prototype.showLoading = function() {
2218 - if(typeof this.___showsLoading___ === 'undefined' && this.chart_created === false && this.enabled === true) {
2219 - if(this.debug === true)
2220 - this.log('show loading...');
2074 + this.requested_after = after * 1000;
2075 + this.requested_before = before * 1000;
2076
2222 - this.element_message.style.display = 'none';
2223 - if(this.element_chart !== null) this.element_chart.style.display = 'none';
2224 - if(this.element_legend !== null) this.element_legend.style.display = 'none';
2225 - if(this.element_loading !== null) this.element_loading.style.display = 'inline-block';
2226 - this.___showsLoading___ = true;
2227 - this.___isHidden___ = undefined;
2228 - this.element_loading.innerHTML = 'chart ' + this.id + ' is loading...';
2229 - this.tm.last_hidden = new Date().getTime();
2230 - }
2231 - }
2077 + this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2078
2233 - chartState.prototype.isVisible = function() {
2234 - // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2079 + // build the data URL
2080 + this.data_url = this.chart.data_url;
2081 + this.data_url += "&format=" + this.library.format();
2082 + this.data_url += "&points=" + this.data_points.toString();
2083 + this.data_url += "&group=" + this.method;
2084 + this.data_url += "&options=" + this.library.options();
2085 + this.data_url += '|jsonwrap';
2086
2236 - // caching - we do not evaluate the charts visibility
2237 - // if the page has not been scrolled since the last check
2238 - if(this.tm.last_visible_check > NETDATA.options.last_page_scroll) {
2239 - if(this.debug === true)
2240 - this.log('isVisible: ' + this.___isVisible___);
2087 + if(NETDATA.options.current.eliminate_zero_dimensions === true)
2088 + this.data_url += '|nonzero';
2089
2242 - return this.___isVisible___;
2243 - }
2090 + if(after)
2091 + this.data_url += "&after=" + after.toString();
2092
2245 - this.tm.last_visible_check = new Date().getTime();
2093 + if(before)
2094 + this.data_url += "&before=" + before.toString();
2095
2247 - var wh = window.innerHeight;
2248 - var x = this.element.getBoundingClientRect();
2249 - var ret = 0;
2250 - var tolerance = 0;
2096 + if(this.dimensions)
2097 + this.data_url += "&dimensions=" + this.dimensions;
2098
2252 - if(x.top < 0 && -x.top > x.height) {
2253 - // the chart is entirely above
2254 - ret = -x.top - x.height;
2099 + if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
2100 + this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
2101 }
2256 - else if(x.top > wh) {
2257 - // the chart is entirely below
2258 - ret = x.top - wh;
2102 +
2103 + this.redrawChart = function() {
2104 + if(this.data !== null)
2105 + this.updateChartWithData(this.data);
2106 }
2107
2261 - if(ret > tolerance) {
2262 - // the chart is too far
2263 - this.___isVisible___ = false;
2264 - if(this.chart_created === true) this.hideChart();
2265 -
2108 + this.updateChartWithData = function(data) {
2109 if(this.debug === true)
2267 - this.log('isVisible: ' + this.___isVisible___);
2110 + this.log('got data from netdata server');
2111
2269 - return this.___isVisible___;
2270 - }
2271 - else {
2272 - // the chart is inside or very close
2273 - this.___isVisible___ = true;
2274 - this.unhideChart();
2275 -
2276 - if(this.debug === true)
2277 - this.log('isVisible: ' + this.___isVisible___);
2112 + // this may force the chart to be re-created
2113 + resizeChart();
2114
2279 - return this.___isVisible___;
2280 - }
2281 - }
2115 + this.data = data;
2116 + this.updates_counter++;
2117
2283 - chartState.prototype.isAutoRefreshed = function() {
2284 - return (this.current.autorefresh);
2285 - }
2118 + var started = new Date().getTime();
2119
2287 - chartState.prototype.canBeAutoRefreshed = function() {
2288 - now = new Date().getTime();
2120 + // if the result is JSON, find the latest update-every
2121 + if(typeof data === 'object') {
2122 + if(typeof data.view_update_every !== 'undefined')
2123 + this.data_update_every = data.view_update_every * 1000;
2124
2290 - if(this.enabled === false) {
2291 - if(this.debug === true)
2292 - this.log('I am not enabled');
2125 + if(typeof data.after !== 'undefined')
2126 + this.data_after = data.after * 1000;
2127
2294 - return false;
2295 - }
2128 + if(typeof data.before !== 'undefined')
2129 + this.data_before = data.before * 1000;
2130
2297 - if(this.library === null || this.library.enabled === false) {
2298 - this.error('charting library "' + this.library_name + '" is not available');
2299 - if(this.debug === true)
2300 - this.log('My chart library ' + this.library_name + ' is not available');
2131 + if(typeof data.first_entry !== 'undefined')
2132 + this.netdata_first = data.first_entry * 1000;
2133
2302 - return false;
2303 - }
2134 + if(typeof data.last_entry !== 'undefined')
2135 + this.netdata_last = data.last_entry * 1000;
2136
2305 - if(this.isVisible() === false) {
2306 - if(NETDATA.options.debug.visibility === true || this.debug === true)
2307 - this.log('I am not visible');
2137 + if(typeof data.points !== 'undefined')
2138 + this.data_points = data.points;
2139
2309 - return false;
2310 - }
2311 -
2312 - if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
2313 - if(this.debug === true)
2314 - this.log('timed force update detected - allowing this update');
2140 + data.state = this;
2141 + }
2142
2316 - this.current.force_update_at = 0;
2317 - return true;
2318 - }
2143 + if(this.debug === true) {
2144 + this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
2145 +
2146 + if(this.current.force_after_ms)
2147 + this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
2148 + else
2149 + this.log('STATUS: forced: unset');
2150
2320 - if(this.isAutoRefreshed() === true) {
2321 - // allow the first update, even if the page is not visible
2322 - if(this.updates_counter && NETDATA.options.page_is_visible === false) {
2323 - if(NETDATA.options.debug.focus === true || this.debug === true)
2324 - this.log('canBeAutoRefreshed(): page does not have focus');
2151 + this.log('STATUS: requested: ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
2152 + this.log('STATUS: rendered : ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
2153 + this.log('STATUS: points : ' + (this.data_points).toString());
2154 + }
2155
2326 - return false;
2156 + if(this.data_points === 0) {
2157 + noDataToShow();
2158 + return;
2159 }
2160
2329 - if(this.needsRecreation() === true) {
2161 + if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
2162 if(this.debug === true)
2331 - this.log('canBeAutoRefreshed(): needs re-creation.');
2163 + this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
2164
2333 - return true;
2165 + this.chart_created = false;
2166 }
2167
2336 - // options valid only for autoRefresh()
2337 - if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
2338 - if(NETDATA.globalPanAndZoom.isActive()) {
2339 - if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
2340 - if(this.debug === true)
2341 - this.log('canBeAutoRefreshed(): global panning: I need an update.');
2168 + // check and update the legend
2169 + this.legendUpdateDOM();
2170
2343 - return true;
2344 - }
2345 - else {
2346 - if(this.debug === true)
2347 - this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
2171 + if(this.chart_created === true
2172 + && typeof this.library.update === 'function') {
2173
2349 - return false;
2174 + if(this.debug === true)
2175 + this.log('updating chart...');
2176 +
2177 + this.updates_since_last_creation++;
2178 + if(NETDATA.options.debug.chart_errors === true) {
2179 + this.library.update(this, data);
2180 + }
2181 + else {
2182 + try {
2183 + this.library.update(this, data);
2184 + }
2185 + catch(err) {
2186 + error('chart failed to be updated as ' + this.library_name);
2187 }
2188 }
2189 + }
2190 + else {
2191 + if(this.debug === true)
2192 + this.log('creating chart...');
2193
2353 - if(this.selected === true) {
2354 - if(this.debug === true)
2355 - this.log('canBeAutoRefreshed(): I have a selection in place.');
2356 -
2357 - return false;
2194 + if(NETDATA.options.debug.chart_errors === true) {
2195 + this.library.create(this, data);
2196 + this.chart_created = true;
2197 + this.updates_since_last_creation = 0;
2198 }
2359 -
2360 - if(this.paused === true) {
2361 - if(this.debug === true)
2362 - this.log('canBeAutoRefreshed(): I am paused.');
2363 -
2364 - return false;
2199 + else {
2200 + try {
2201 + this.library.create(this, data);
2202 + this.chart_created = true;
2203 + this.updates_since_last_creation = 0;
2204 + }
2205 + catch(err) {
2206 + error('chart failed to be created as ' + this.library_name);
2207 + }
2208 }
2209 + }
2210 + hideMessage();
2211 + NETDATA.globalSelectionSync.stop();
2212
2367 - if(now - this.tm.last_autorefreshed >= this.data_update_every) {
2368 - if(this.debug === true)
2369 - this.log('canBeAutoRefreshed(): It is time to update me.');
2213 + // this.legendShowLatestValues();
2214
2371 - return true;
2372 - }
2215 + // update the performance counters
2216 + var now = new Date().getTime();
2217 + this.tm.last_updated = now;
2218 +
2219 + // don't update last_autorefreshed if this chart is
2220 + // forced to be updated with global PanAndZoom
2221 + if(NETDATA.globalPanAndZoom.isActive())
2222 + this.tm.last_autorefreshed = 0;
2223 + else {
2224 + if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes)
2225 + this.tm.last_autorefreshed = Math.round(now / this.data_update_every) * this.data_update_every;
2226 + else
2227 + this.tm.last_autorefreshed = now;
2228 }
2374 - }
2229
2376 - return false;
2377 - }
2230 + this.refresh_dt_ms = now - started;
2231 + NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
2232
2379 - chartState.prototype.autoRefresh = function(callback) {
2380 - if(this.canBeAutoRefreshed() === true) {
2381 - this.updateChart(callback);
2382 - }
2383 - else {
2384 - if(typeof callback !== 'undefined')
2385 - callback();
2233 + if(this.refresh_dt_element !== null)
2234 + this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
2235 }
2387 - }
2236
2389 - chartState.prototype._defaultsFromDownloadedChart = function(chart) {
2390 - this.chart = chart;
2391 - this.chart_url = chart.url;
2392 - this.data_update_every = chart.update_every * 1000;
2393 - this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2394 - this.tm.last_info_downloaded = new Date().getTime();
2395 - }
2237 + this.updateChart = function(callback) {
2238 + // due to late initialization of charts and libraries
2239 + // we need to check this too
2240 + if(this.enabled === false) {
2241 + if(this.debug === true)
2242 + this.log('I am not enabled');
2243
2397 - // fetch the chart description from the netdata server
2398 - chartState.prototype.getChart = function(callback) {
2399 - this.chart = NETDATA.chartRegistry.get(this.host, this.id);
2400 - if(this.chart) {
2401 - this._defaultsFromDownloadedChart(this.chart);
2402 - if(typeof callback === 'function') callback();
2403 - }
2404 - else {
2405 - this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
2244 + if(typeof callback === 'function') callback();
2245 + return false;
2246 + }
2247
2407 - if(this.debug === true)
2408 - this.log('downloading ' + this.chart_url);
2248 + if(this.chart === null) {
2249 + this.getChart(function() { that.updateChart(callback); });
2250 + return;
2251 + }
2252
2410 - var self = this;
2253 + if(this.library.initialized === false) {
2254 + if(this.library.enabled === true) {
2255 + this.library.initialize(function() { that.updateChart(callback); });
2256 + return;
2257 + }
2258 + else {
2259 + error('chart library "' + this.library_name + '" is not available.');
2260 + if(typeof callback === 'function') callback();
2261 + return false;
2262 + }
2263 + }
2264 +
2265 + this.clearSelection();
2266 + this.chartURL();
2267
2412 - $.ajax( {
2413 - url: this.chart_url,
2268 + if(this.debug === true)
2269 + this.log('updating from ' + this.data_url);
2270 +
2271 + this.xhr = $.ajax( {
2272 + url: this.data_url,
2273 crossDomain: NETDATA.options.crossDomainAjax,
2274 cache: false,
2275 async: true
2276 })
2418 - .done(function(chart) {
2419 - chart.url = self.chart_url;
2420 - chart.data_url = (self.host + chart.data_url);
2421 - self._defaultsFromDownloadedChart(chart);
2422 - NETDATA.chartRegistry.add(self.host, self.id, chart);
2277 + .success(function(data) {
2278 + if(that.debug === true)
2279 + that.log('data received. updating chart.');
2280 +
2281 + that.updateChartWithData(data);
2282 })
2283 .fail(function() {
2425 - NETDATA.error(404, self.chart_url);
2426 - self.error('chart not found on url "' + self.chart_url + '"');
2284 + error('data download failed for url: ' + that.data_url);
2285 })
2286 .always(function() {
2287 if(typeof callback === 'function') callback();
2288 });
2289 }
2432 - }
2290
2434 - // resize the chart to its real dimensions
2435 - // as given by the caller
2436 - chartState.prototype.sizeChart = function() {
2437 - if(this.hasLegend() === true)
2438 - this.element.className += " netdata-container-with-legend";
2439 - else
2440 - this.element.className += " netdata-container";
2291 + this.isVisible = function() {
2292 + // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll);
2293
2442 - if(this.debug === true)
2443 - this.log('sizing element');
2294 + // caching - we do not evaluate the charts visibility
2295 + // if the page has not been scrolled since the last check
2296 + if(this.tm.last_visible_check > NETDATA.options.last_page_scroll) {
2297 + if(this.debug === true)
2298 + this.log('isVisible: ' + this.___isVisible___);
2299
2445 - if(this.width !== 0)
2446 - $(this.element).css('width', this.width);
2300 + return this.___isVisible___;
2301 + }
2302
2448 - if(this.height !== 0)
2449 - $(this.element).css('height', this.height);
2303 + this.tm.last_visible_check = new Date().getTime();
2304
2451 - if(NETDATA.chartDefaults.min_width !== null)
2452 - $(this.element).css('min-width', NETDATA.chartDefaults.min_width);
2453 - }
2305 + var wh = window.innerHeight;
2306 + var x = this.element.getBoundingClientRect();
2307 + var ret = 0;
2308 + var tolerance = 0;
2309
2455 - chartState.prototype.noData = function() {
2456 - if(this.dom_created === false)
2457 - this.createChartDOM();
2310 + if(x.top < 0 && -x.top > x.height) {
2311 + // the chart is entirely above
2312 + ret = -x.top - x.height;
2313 + }
2314 + else if(x.top > wh) {
2315 + // the chart is entirely below
2316 + ret = x.top - wh;
2317 + }
2318
2459 - this.tm.last_autorefreshed = new Date().getTime();
2460 - this.data_update_every = 30 * 1000;
2461 - }
2319 + if(ret > tolerance) {
2320 + // the chart is too far
2321
2463 - // show a message in the chart
2464 - chartState.prototype.message = function(type, msg) {
2465 - this.hideChart();
2466 - this.element_message.innerHTML = msg;
2322 + hideChart();
2323 + this.___isVisible___ = false;
2324 +
2325 + if(this.debug === true)
2326 + this.log('isVisible: ' + this.___isVisible___);
2327
2468 - if(this.debug === null)
2469 - this.log(msg);
2470 - }
2328 + return this.___isVisible___;
2329 + }
2330 + else {
2331 + // the chart is inside or very close
2332
2472 - // show an error on the chart and stop it forever
2473 - chartState.prototype.error = function(msg) {
2474 - this.message('error', this.id + ': ' + msg);
2475 - this.enabled = false;
2476 - }
2333 + unhideChart();
2334 + this.___isVisible___ = true;
2335 +
2336 + if(this.debug === true)
2337 + this.log('isVisible: ' + this.___isVisible___);
2338
2478 - // show a message indicating the chart is loading
2479 - chartState.prototype.info = function(msg) {
2480 - this.message('info', this.id + ': ' + msg);
2481 - }
2339 + return this.___isVisible___;
2340 + }
2341 + }
2342
2483 - chartState.prototype.init = function() {
2484 - this.element.innerHTML = '';
2343 + this.isAutoRefreshed = function() {
2344 + return (this.current.autorefresh);
2345 + }
2346
2486 - this.element_message = document.createElement('div');
2487 - this.element_message.className += ' netdata-message';
2488 - this.element.appendChild(this.element_message);
2347 + this.canBeAutoRefreshed = function() {
2348 + now = new Date().getTime();
2349
2490 - this.element_loading = document.createElement('div');
2491 - this.element_loading.className += ' netdata-chart-is-loading';
2492 - this.element.appendChild(this.element_loading);
2350 + if(this.enabled === false) {
2351 + if(this.debug === true)
2352 + this.log('I am not enabled');
2353
2494 - if(this.debug === null)
2495 - this.log('created');
2354 + return false;
2355 + }
2356
2497 - // make sure the host does not end with /
2498 - // all netdata API requests use absolute paths
2499 - while(this.host.slice(-1) === '/')
2500 - this.host = this.host.substring(0, this.host.length - 1);
2357 + if(this.library === null || this.library.enabled === false) {
2358 + error('charting library "' + this.library_name + '" is not available');
2359 + if(this.debug === true)
2360 + this.log('My chart library ' + this.library_name + ' is not available');
2361
2502 - // check the requested library is available
2503 - // we don't initialize it here - it will be initialized when
2504 - // this chart will be first used
2505 - if(typeof NETDATA.chartLibraries[this.library_name] === 'undefined') {
2506 - NETDATA.error(402, this.library_name);
2507 - this.error('chart library "' + this.library_name + '" is not found');
2362 + return false;
2363 + }
2364 +
2365 + if(this.isVisible() === false) {
2366 + if(NETDATA.options.debug.visibility === true || this.debug === true)
2367 + this.log('I am not visible');
2368 +
2369 + return false;
2370 + }
2371 +
2372 + if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
2373 + if(this.debug === true)
2374 + this.log('timed force update detected - allowing this update');
2375 +
2376 + this.current.force_update_at = 0;
2377 + return true;
2378 + }
2379 +
2380 + if(this.isAutoRefreshed() === true) {
2381 + // allow the first update, even if the page is not visible
2382 + if(this.updates_counter && NETDATA.options.page_is_visible === false) {
2383 + if(NETDATA.options.debug.focus === true || this.debug === true)
2384 + this.log('canBeAutoRefreshed(): page does not have focus');
2385 +
2386 + return false;
2387 + }
2388 +
2389 + if(this.needsRecreation() === true) {
2390 + if(this.debug === true)
2391 + this.log('canBeAutoRefreshed(): needs re-creation.');
2392 +
2393 + return true;
2394 + }
2395 +
2396 + // options valid only for autoRefresh()
2397 + if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
2398 + if(NETDATA.globalPanAndZoom.isActive()) {
2399 + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
2400 + if(this.debug === true)
2401 + this.log('canBeAutoRefreshed(): global panning: I need an update.');
2402 +
2403 + return true;
2404 + }
2405 + else {
2406 + if(this.debug === true)
2407 + this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
2408 +
2409 + return false;
2410 + }
2411 + }
2412 +
2413 + if(this.selected === true) {
2414 + if(this.debug === true)
2415 + this.log('canBeAutoRefreshed(): I have a selection in place.');
2416 +
2417 + return false;
2418 + }
2419 +
2420 + if(this.paused === true) {
2421 + if(this.debug === true)
2422 + this.log('canBeAutoRefreshed(): I am paused.');
2423 +
2424 + return false;
2425 + }
2426 +
2427 + if(now - this.tm.last_autorefreshed >= this.data_update_every) {
2428 + if(this.debug === true)
2429 + this.log('canBeAutoRefreshed(): It is time to update me.');
2430 +
2431 + return true;
2432 + }
2433 + }
2434 + }
2435 +
2436 + return false;
2437 }
2509 - else if(NETDATA.chartLibraries[this.library_name].enabled === false) {
2510 - NETDATA.error(403, this.library_name);
2511 - this.error('chart library "' + this.library_name + '" is not enabled');
2438 +
2439 + this.autoRefresh = function(callback) {
2440 + if(this.canBeAutoRefreshed() === true) {
2441 + this.updateChart(callback);
2442 + }
2443 + else {
2444 + if(typeof callback !== 'undefined')
2445 + callback();
2446 + }
2447 }
2513 - else
2514 - this.library = NETDATA.chartLibraries[this.library_name];
2448
2516 - this.sizeChart();
2449 + this._defaultsFromDownloadedChart = function(chart) {
2450 + this.chart = chart;
2451 + this.chart_url = chart.url;
2452 + this.data_update_every = chart.update_every * 1000;
2453 + this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
2454 + this.tm.last_info_downloaded = new Date().getTime();
2455 + }
2456
2518 - // if we need to report the rendering speed
2519 - // find the element that needs to be updated
2520 - if(this.refresh_dt_element_name)
2521 - this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null;
2457 + // fetch the chart description from the netdata server
2458 + this.getChart = function(callback) {
2459 + this.chart = NETDATA.chartRegistry.get(this.host, this.id);
2460 + if(this.chart) {
2461 + this._defaultsFromDownloadedChart(this.chart);
2462 + if(typeof callback === 'function') callback();
2463 + }
2464 + else {
2465 + this.chart_url = this.host + "/api/v1/chart?chart=" + this.id;
2466 +
2467 + if(this.debug === true)
2468 + this.log('downloading ' + this.chart_url);
2469 +
2470 + $.ajax( {
2471 + url: this.chart_url,
2472 + crossDomain: NETDATA.options.crossDomainAjax,
2473 + cache: false,
2474 + async: true
2475 + })
2476 + .done(function(chart) {
2477 + chart.url = that.chart_url;
2478 + chart.data_url = (that.host + chart.data_url);
2479 + that._defaultsFromDownloadedChart(chart);
2480 + NETDATA.chartRegistry.add(that.host, that.id, chart);
2481 + })
2482 + .fail(function() {
2483 + NETDATA.error(404, that.chart_url);
2484 + error('chart not found on url "' + that.chart_url + '"');
2485 + })
2486 + .always(function() {
2487 + if(typeof callback === 'function') callback();
2488 + });
2489 + }
2490 + }
2491 +
2492 + // ============================================================================================================
2493 + // INITIALIZATION
2494
2523 - // the default mode for all charts
2524 - this.setMode('auto');
2495 + init();
2496 }
2497
2498 // get or create a chart state, given a DOM element
@@ -3283,9 +3254,10 @@
3254 y: {
3255 pixelsPerLabel: 15,
3256 valueFormatter: function (x) {
3257 + // we format legends with the state object
3258 + // no need to do anything here
3259 // return (Math.round(x*100) / 100).toLocaleString();
3287 - //return state.legendFormatValue(x);
3288 - //FIXME
3260 + // return state.legendFormatValue(x);
3261 return x;
3262 }
3263 }
@@ -3702,7 +3674,7 @@
3674 NETDATA.morrisChartCreate = function(state, data) {
3675
3676 state.morris_options = {
3705 - element: state.element_chart_id,
3677 + element: state.element_chart.id,
3678 data: data.result.data,
3679 xkey: 'time',
3680 ykeys: data.dimension_names,
web/index.html
+1 -1
@@ -253,7 +253,7 @@
253 <!-- <script> netdataServer = "http://box:19999"; </script> -->
254
255 <!-- load the dashboard manager - it will do the rest -->
256 - <script type="text/javascript" src="dashboard.js?v8"></script>
256 + <script type="text/javascript" src="dashboard.js?v9"></script>
257 </head>
258
259 <body data-spy="scroll" data-target="#sidebar">