From: Costa Tsaousis (ktsaou) Date: Tue, 29 Dec 2015 02:21:19 +0000 (+0200) Subject: fixed minor issues throughout the code (mainly types); dashboard has now a watermark... X-Git-Tag: v1.0rc~116 X-Git-Url: https://arthur.barton.de/gitweb/?p=netdata.git;a=commitdiff_plain;h=f4456c3d41da3d23520106fdb8b5d9f66267a3cb 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 --- diff --git a/src/appconfig.c b/src/appconfig.c index 3ba7b919..7cbb053b 100755 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -202,7 +202,7 @@ int load_config(char *filename, int overwrite_used) continue; } - int len = strlen(s); + int len = (int) strlen(s); if(*s == '[' && s[len - 1] == ']') { // new section s[len - 1] = '\0'; diff --git a/src/apps_plugin.c b/src/apps_plugin.c index a7285c4f..83d5488c 100755 --- a/src/apps_plugin.c +++ b/src/apps_plugin.c @@ -45,8 +45,6 @@ int debug = 0; int update_every = 1; unsigned long long file_counter = 0; -#define PROC_BUFFER 4096 - // ---------------------------------------------------------------------------- // memory debugger @@ -64,14 +62,14 @@ struct allocations { void *mark_allocation(void *allocated_ptr, size_t size_without_overheads) { uint32_t *real_ptr = (uint32_t *)allocated_ptr; real_ptr[0] = MALLOC_MARK; - real_ptr[1] = size_without_overheads; + real_ptr[1] = (uint32_t) size_without_overheads; uint32_t *end_ptr = (uint32_t *)(allocated_ptr + MALLOC_PREFIX + size_without_overheads); end_ptr[0] = MALLOC_MARK; // fprintf(stderr, "MEMORY_POINTER: Allocated at %p, returning %p.\n", allocated_ptr, (void *)(allocated_ptr + MALLOC_PREFIX)); - return (void *)(allocated_ptr + MALLOC_PREFIX); + return allocated_ptr + MALLOC_PREFIX; } 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) { unsigned long long get_hertz(void) { - unsigned long long hz = 1; + unsigned long long myhz = 1; #ifdef _SC_CLK_TCK - if((hz = sysconf(_SC_CLK_TCK)) > 0) { - return hz; + if((myhz = (unsigned long long int) sysconf(_SC_CLK_TCK)) > 0) { + return myhz; } #endif #ifdef HZ - hz = (unsigned long long)HZ; /* */ + myhz = HZ; /* */ #else /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; #endif - error("Unknown HZ value. Assuming %llu.", hz); - return hz; + error("Unknown HZ value. Assuming %llu.", myhz); + return myhz; } @@ -650,7 +648,7 @@ int read_proc_pid_stat(struct pid_stat *p) { // p->pid = atol(procfile_lineword(ff, 0, 0+i)); // comm is at 1 // p->state = *(procfile_lineword(ff, 0, 2+i)); - p->ppid = atol(procfile_lineword(ff, 0, 3+i)); + p->ppid = (int32_t) atol(procfile_lineword(ff, 0, 3 + i)); // p->pgrp = atol(procfile_lineword(ff, 0, 4+i)); // p->session = atol(procfile_lineword(ff, 0, 5+i)); // p->tty_nr = atol(procfile_lineword(ff, 0, 6+i)); @@ -666,7 +664,7 @@ int read_proc_pid_stat(struct pid_stat *p) { p->cstime = strtoull(procfile_lineword(ff, 0, 16+i), NULL, 10); // p->priority = strtoull(procfile_lineword(ff, 0, 17+i), NULL, 10); // p->nice = strtoull(procfile_lineword(ff, 0, 18+i), NULL, 10); - p->num_threads = atol(procfile_lineword(ff, 0, 19+i)); + p->num_threads = (int32_t) atol(procfile_lineword(ff, 0, 19 + i)); // p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20+i), NULL, 10); // p->starttime = strtoull(procfile_lineword(ff, 0, 21+i), NULL, 10); // p->vsize = strtoull(procfile_lineword(ff, 0, 22+i), NULL, 10); @@ -809,8 +807,8 @@ struct file_descriptor { uint32_t hash; const char *name; int type; - long count; - long pos; + int count; + int pos; } *all_files = NULL; int all_files_len = 0; @@ -871,7 +869,7 @@ void file_descriptor_not_used(int id) return; } - if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %ld).\n", id, all_files[id].count); + if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count); if(all_files[id].count > 0) { all_files[id].count--; @@ -889,7 +887,7 @@ void file_descriptor_not_used(int id) else error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size); } -unsigned long file_descriptor_find_or_add(const char *name) +int file_descriptor_find_or_add(const char *name) { static int last_pos = 0; uint32_t hash = simple_hash(name); @@ -899,7 +897,7 @@ unsigned long file_descriptor_find_or_add(const char *name) struct file_descriptor *fd = file_descriptor_find(name, hash); if(fd) { // found - if(debug) fprintf(stderr, "apps.plugin: >> found on slot %ld\n", fd->pos); + if(debug) fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos); fd->count++; return fd->pos; } @@ -968,7 +966,7 @@ unsigned long file_descriptor_find_or_add(const char *name) // else we have an empty slot in 'c' - int type = FILETYPE_OTHER; + int type; if(name[0] == '/') type = FILETYPE_FILE; else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE; else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET; @@ -1041,7 +1039,7 @@ int update_from_proc(void) while((file = readdir(dir))) { char *endptr = file->d_name; - pid_t pid = strtoul(file->d_name, &endptr, 10); + pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10); if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue; p = get_pid_entry(pid); @@ -1119,7 +1117,7 @@ int update_from_proc(void) if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; // check if the fds array is small - int fdid = atol(de->d_name); + int fdid = atoi(de->d_name); if(fdid < 0) continue; if(fdid >= p->fds_size) { // it is small, extend it @@ -1139,7 +1137,7 @@ int update_from_proc(void) // we don't know this fd, get it sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name); - int l = readlink(fdname, linkname, FILENAME_MAX); + ssize_t l = readlink(fdname, linkname, FILENAME_MAX); if(l == -1) { if(debug || (p->target && p->target->debug)) { if(!count_errors++ || debug || (p->target && p->target->debug)) @@ -1337,7 +1335,7 @@ void update_statistics(void) for (w = target_root; w ; w = w->next) { targets++; - w->fds = calloc(sizeof(int), all_files_size); + w->fds = calloc(sizeof(int), (size_t) all_files_size); if(!w->fds) error("Cannot allocate memory for fds in %s", w->name); @@ -1748,7 +1746,7 @@ void show_charts(void) for (w = target_root; w ; w = w->next) { if(w->target || (!w->processes && !w->exposed)) continue; - fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz), w->hidden?"hidden,noreset":"noreset"); + fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, Hertz, w->hidden ? "hidden,noreset" : "noreset"); } 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) procfile_adaptive_initial_allocation = 1; - unsigned long started_t = time(NULL), current_t; + time_t started_t = time(NULL); + time_t current_t; Hertz = get_hertz(); pid_max = get_pid_max(); processors = get_processors(); parse_args(argc, argv); - all_pids = calloc(sizeof(struct pid_stat *), pid_max); + all_pids = calloc(sizeof(struct pid_stat *), (size_t) pid_max); if(!all_pids) { error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max); printf("DISABLE\n"); @@ -1957,7 +1956,7 @@ int main(int argc, char **argv) if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec; else susec = update_every * 1000000ULL / 2; - usleep(susec); + usleep((__useconds_t) susec); bcopy(&now, &last, sizeof(struct timeval)); current_t = time(NULL); diff --git a/src/avl.c b/src/avl.c index f5d67dc2..4eb0ce0e 100755 --- a/src/avl.c +++ b/src/avl.c @@ -319,7 +319,7 @@ int avl_removeroot(avl_tree* t) { * returns the last value returned by iterator or 0 if there were no calls * Warning: a<=b must hold */ -int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) { +int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) { int x, c = 0; if (!t->root) return 0; @@ -361,7 +361,7 @@ int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) { return c; } -int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret) { +int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) { #ifdef AVL_LOCK_WITH_MUTEX pthread_mutex_lock(&t->mutex); #else diff --git a/src/avl.h b/src/avl.h index 888eed19..cbcc4121 100755 --- a/src/avl.h +++ b/src/avl.h @@ -12,11 +12,11 @@ * Released under GNU General Public License (GPL) version 2 * */ -#include - #ifndef _AVL_H #define _AVL_H 1 +#include + // #define AVL_LOCK_WITH_MUTEX 1 /* Data structures */ @@ -65,13 +65,13 @@ int avl_removeroot(avl_tree* t); * returns the last value returned by iterator or 0 if there were no calls * Warning: a<=b must hold */ -int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl* a), avl** ret); +int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret); /* Iterate through elements in t equal to a * for each element calls iter(a) until it returns 0 * returns the last value returned by iterator or 0 if there were no calls */ -int avl_search(avl_tree* t, avl* a, int (*iter)(avl* a), avl** ret); +int avl_search(avl_tree* t, avl* a, int (*iter)(avl*), avl** ret); /* Initialize the avl_tree */ diff --git a/src/common.c b/src/common.c index bf713195..bde93500 100755 --- a/src/common.c +++ b/src/common.c @@ -99,7 +99,7 @@ char *trim(char *s) if(!*s || *s == '#') return NULL; // skip tailing spaces - int c = strlen(s) - 1; + long c = (long) strlen(s) - 1; while(c >= 0 && isspace(s[c])) { s[c] = '\0'; c--; @@ -109,7 +109,7 @@ char *trim(char *s) return s; } -void *mymmap(const char *filename, unsigned long size, int flags, int ksm) +void *mymmap(const char *filename, size_t size, int flags, int ksm) { int fd; void *mem = NULL; @@ -139,7 +139,7 @@ void *mymmap(const char *filename, unsigned long size, int flags, int ksm) mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags|MAP_ANONYMOUS, -1, 0); if(mem) { if(lseek(fd, 0, SEEK_SET) == 0) { - if(read(fd, mem, size) != size) + if(read(fd, mem, size) != (ssize_t)size) error("Cannot read from file '%s'", filename); } else diff --git a/src/common.h b/src/common.h index ba00af74..9d8836f8 100755 --- a/src/common.h +++ b/src/common.h @@ -17,7 +17,7 @@ extern void strreverse(char* begin, char* end); extern char *mystrsep(char **ptr, char *s); extern char *trim(char *s); -extern void *mymmap(const char *filename, unsigned long size, int flags, int ksm); +extern void *mymmap(const char *filename, size_t size, int flags, int ksm); extern int savememory(const char *filename, void *mem, unsigned long size); extern int fd_is_valid(int fd); @@ -26,7 +26,7 @@ extern char *global_host_prefix; extern int enable_ksm; /* Number of ticks per second */ -#define HZ hz +#define HZ myhz extern unsigned int hz; extern void get_HZ(void); diff --git a/src/daemon.c b/src/daemon.c index 4cf89ec3..26881479 100755 --- a/src/daemon.c +++ b/src/daemon.c @@ -137,7 +137,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch fflush(NULL); // open the files before forking - int input_fd = -1, output_fd = -1, error_fd = -1, dev_null = -1; + int input_fd = -1, output_fd = -1, error_fd = -1, dev_null; if(input && *input) { 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 // close all files if(close_all_files) { int i; - for(i = sysconf(_SC_OPEN_MAX) - 1; i > 0; i--) + for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--) if( ((access_fd && i != *access_fd) || !access_fd) && i != dev_null @@ -300,7 +300,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch if(fd >= 0) { char b[100]; sprintf(b, "%d\n", getpid()); - int i = write(fd, b, strlen(b)); + ssize_t i = write(fd, b, strlen(b)); if(i <= 0) perror("Cannot write pid to file."); close(fd); } diff --git a/src/dictionary.c b/src/dictionary.c index 033717f5..31f4d52e 100755 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -40,7 +40,10 @@ static NAME_VALUE *dictionary_name_value_create(DICTIONARY *dict, const char *na debug(D_DICTIONARY, "Creating name value entry for name '%s', value '%s'.", name, value); NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE)); - if(!nv) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE)); + if(!nv) { + fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE)); + exit(1); + } nv->name = strdup(name); 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) { else { NAME_VALUE *n = dict->values; while(n && n->next && n->next != nv) nv = nv->next; - if(!n || n->next != nv) fatal("Cannot find name_value with name '%s' in dictionary.", nv->name); + if(!n || n->next != nv) { + fatal("Cannot find name_value with name '%s' in dictionary.", nv->name); + exit(1); + } n->next = nv->next; nv->next = NULL; } @@ -86,7 +92,10 @@ DICTIONARY *dictionary_create(void) { debug(D_DICTIONARY, "Creating dictionary."); DICTIONARY *dict = calloc(1, sizeof(DICTIONARY)); - if(!dict) fatal("Cannot allocate DICTIONARY"); + if(!dict) { + fatal("Cannot allocate DICTIONARY"); + exit(1); + } avl_init(&dict->values_index, name_value_compare); pthread_rwlock_init(&dict->rwlock, NULL); @@ -115,7 +124,10 @@ void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t val if(!nv) { debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name); nv = dictionary_name_value_create(dict, name, value, value_len); - if(!nv) fatal("Cannot create name_value."); + if(!nv) { + fatal("Cannot create name_value."); + exit(1); + } return nv->value; } else { diff --git a/src/global_statistics.c b/src/global_statistics.c index 1b8c5d6f..d4a04efd 100755 --- a/src/global_statistics.c +++ b/src/global_statistics.c @@ -3,7 +3,6 @@ #endif #include -#include "common.h" #include "global_statistics.h" struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL }; diff --git a/src/log.c b/src/log.c index ef452d41..7ab3f1a5 100755 --- a/src/log.c +++ b/src/log.c @@ -27,7 +27,6 @@ int access_log_syslog = 1; int error_log_syslog = 1; int output_log_syslog = 1; // debug log - void log_date(FILE *out) { char outstr[200]; diff --git a/src/main.c b/src/main.c index 953a7e66..3f0c5713 100755 --- a/src/main.c +++ b/src/main.c @@ -158,7 +158,7 @@ void kill_childs() if(tc_child_pid) { info("Killing tc-qos-helper procees"); if(killpid(tc_child_pid, SIGTERM) != -1) - waitid(P_PID, tc_child_pid, &info, WEXITED); + waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED); } tc_child_pid = 0; @@ -171,7 +171,7 @@ void kill_childs() if(cd->pid && !cd->obsolete) { debug(D_EXIT, "killing %s plugin process", cd->id); if(killpid(cd->pid, SIGTERM) != -1) - waitid(P_PID, cd->pid, &info, WEXITED); + waitid(P_PID, (id_t) cd->pid, &info, WEXITED); } } @@ -317,7 +317,6 @@ int main(int argc, char **argv) // -------------------------------------------------------------------- - silent = 0; error_log_file = config_get("global", "error log", LOG_DIR "/error.log"); if(strcmp(error_log_file, "syslog") == 0) { error_log_syslog = 1; @@ -326,7 +325,7 @@ int main(int argc, char **argv) else if(strcmp(error_log_file, "none") == 0) { error_log_syslog = 0; error_log_file = NULL; - silent = 1; // optimization - do not even generate debug log entries + // optimization - do not even generate debug log entries } else error_log_syslog = 0; @@ -356,7 +355,7 @@ int main(int argc, char **argv) // -------------------------------------------------------------------- - rrd_default_history_entries = config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES); + rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES); if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) { fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES); rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES; @@ -367,7 +366,7 @@ int main(int argc, char **argv) // -------------------------------------------------------------------- - rrd_update_every = config_get_number("global", "update every", UPDATE_EVERY); + rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY); if(rrd_update_every < 1 || rrd_update_every > 600) { fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", rrd_update_every, UPDATE_EVERY_MAX); rrd_update_every = UPDATE_EVERY; @@ -376,9 +375,9 @@ int main(int argc, char **argv) // let the plugins know the min update_every { - char buffer[50]; - snprintf(buffer, 50, "%d", rrd_update_every); - setenv("NETDATA_UPDATE_EVERY", buffer, 1); + char buf[50]; + snprintf(buf, 50, "%d", rrd_update_every); + setenv("NETDATA_UPDATE_EVERY", buf, 1); } // -------------------------------------------------------------------- @@ -398,9 +397,9 @@ int main(int argc, char **argv) // -------------------------------------------------------------------- - listen_backlog = config_get_number("global", "http port listen backlog", LISTEN_BACKLOG); + listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG); - listen_port = config_get_number("global", "port", LISTEN_PORT); + listen_port = (int) config_get_number("global", "port", LISTEN_PORT); if(listen_port < 1 || listen_port > 65535) { fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT); listen_port = LISTEN_PORT; diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c index b5ec8009..8f55a002 100755 --- a/src/plugin_idlejitter.c +++ b/src/plugin_idlejitter.c @@ -28,7 +28,7 @@ void *cpuidlejitter_main(void *ptr) if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - int sleep_ms = config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS); + int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS); if(sleep_ms <= 0) { config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS); sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS; diff --git a/src/plugin_proc.c b/src/plugin_proc.c index 9d7a48d6..656ac173 100755 --- a/src/plugin_proc.c +++ b/src/plugin_proc.c @@ -213,7 +213,7 @@ void *proc_main(void *ptr) rrdset_done(stbytes); } - usleep(susec); + usleep((useconds_t) susec); // copy current to last bcopy(&now, &last, sizeof(struct timeval)); diff --git a/src/plugin_proc.h b/src/plugin_proc.h index 41ba785c..dcbd8dbb 100755 --- a/src/plugin_proc.h +++ b/src/plugin_proc.h @@ -16,5 +16,6 @@ extern int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt); extern int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt); extern int do_proc_interrupts(int update_every, unsigned long long dt); extern int do_proc_softirqs(int update_every, unsigned long long dt); +extern int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt); #endif /* NETDATA_PLUGIN_PROC_H */ diff --git a/src/plugin_tc.c b/src/plugin_tc.c index 1a4be02d..ae6f4ad1 100755 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -153,7 +153,7 @@ static void tc_device_classes_cleanup(struct tc_device *d) { static int cleanup_every = 999; if(cleanup_every > 0) { - cleanup_every = -config_get_number("plugin:tc", "cleanup unused classes every", 60); + cleanup_every = (int) -config_get_number("plugin:tc", "cleanup unused classes every", 60); if(cleanup_every > 0) cleanup_every = -cleanup_every; if(cleanup_every == 0) cleanup_every = -1; } @@ -598,7 +598,7 @@ void *tc_main(void *ptr) else if(first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0 && device && class) { // debug(D_TC_LOOP, "SENT line '%s'", words[1]); if(words[1] && *words[1]) { - class->bytes = atoll(words[1]); + class->bytes = strtoull(words[1], NULL, 10); class->updated = 1; } else class->bytes = 0; @@ -673,7 +673,7 @@ void *tc_main(void *ptr) return NULL; } - sleep(rrd_update_every); + sleep((unsigned int) rrd_update_every); } return NULL; diff --git a/src/plugins_d.c b/src/plugins_d.c index b99f668d..6aae68e1 100755 --- a/src/plugins_d.c +++ b/src/plugins_d.c @@ -396,10 +396,10 @@ void *pluginsd_worker_thread(void *arg) if(unlikely(!count && cd->enabled)) { error("PLUGINSD: '%s' (pid %d) does not generate usefull output. Waiting a bit before starting it again.", cd->fullfilename, cd->pid); - sleep(cd->update_every * 10); + sleep((unsigned int) (cd->update_every * 10)); } - if(likely(cd->enabled)) sleep(cd->update_every); + if(likely(cd->enabled)) sleep((unsigned int) cd->update_every); else break; } @@ -421,7 +421,7 @@ void *pluginsd_main(void *ptr) char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR); int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1); - int scan_frequency = config_get_number("plugins", "check for new plugins every", 60); + int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60); DIR *dir = NULL; struct dirent *file = NULL; struct plugind *cd; @@ -447,7 +447,7 @@ void *pluginsd_main(void *ptr) if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue; - int len = strlen(file->d_name); + int len = (int) strlen(file->d_name); if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue; if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) { 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) snprintf(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename); cd->enabled = enabled; - cd->update_every = config_get_number(cd->id, "update every", rrd_update_every); + cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every); cd->started_t = time(NULL); char *def = ""; @@ -508,7 +508,7 @@ void *pluginsd_main(void *ptr) } closedir(dir); - sleep(scan_frequency); + sleep((unsigned int) scan_frequency); } return NULL; diff --git a/src/popen.c b/src/popen.c index 9449c64f..4f721f5e 100755 --- a/src/popen.c +++ b/src/popen.c @@ -78,7 +78,7 @@ FILE *mypopen(const char *command, pid_t *pidptr) // close all files int i; - for(i = sysconf(_SC_OPEN_MAX) - 1; i > 0; i--) + for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--) if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i); // move the pipe to stdout @@ -128,7 +128,7 @@ void mypclose(FILE *fp, pid_t pid) { fclose(fp); siginfo_t info; - if(waitid(P_PID, pid, &info, WEXITED) != -1) { + if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) { switch(info.si_code) { case CLD_EXITED: error("pid %d exited with code %d.", info.si_pid, info.si_status); diff --git a/src/proc_diskstats.c b/src/proc_diskstats.c index 8e4da0eb..15e38e43 100755 --- a/src/proc_diskstats.c +++ b/src/proc_diskstats.c @@ -12,10 +12,7 @@ #include "rrd.h" #include "plugin_proc.h" -#define RRD_TYPE_DISK "disk" -#define RRD_TYPE_DISK_LEN strlen(RRD_TYPE_DISK) - -#define MAX_PROC_DISKSTATS_LINE 4096 +#define RRD_TYPE_DISK "disk" int do_proc_diskstats(int update_every, unsigned long long dt) { static procfile *ff = NULL; diff --git a/src/procfile.c b/src/procfile.c index 5bd1b10e..7a485795 100755 --- a/src/procfile.c +++ b/src/procfile.c @@ -235,7 +235,7 @@ cleanup: procfile *procfile_readall(procfile *ff) { debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename); - ssize_t s, r = 1, x = ff->size; + ssize_t s, r = 1, x; ff->len = 0; while(likely(r > 0)) { @@ -253,7 +253,6 @@ procfile *procfile_readall(procfile *ff) { } ff = new; ff->size += PROCFILE_INCREMENT_BUFFER; - x = PROCFILE_INCREMENT_BUFFER; } debug(D_PROCFILE, "Reading file '%s', from position %ld with length %ld", ff->filename, s, ff->size - s); diff --git a/src/rrd.c b/src/rrd.c index 3881d513..6a08a149 100755 --- a/src/rrd.c +++ b/src/rrd.c @@ -437,7 +437,8 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const st->last_collected_time.tv_usec = 0; st->counter_done = 0; - st->gap_when_lost_iterations_above = config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2; + st->gap_when_lost_iterations_above = (int) ( + config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2); avl_init(&st->dimensions_index, rrddim_compare); @@ -598,7 +599,7 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) { debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name); - RRDDIM *i = st->dimensions, *last = NULL; + RRDDIM *i, *last = NULL; for(i = st->dimensions; i && i != rd ; i = i->next) last = i; if(!i) { @@ -606,8 +607,9 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) return; } - if(last) last = i->next; - else st->dimensions = i->next; + if(last) last->next = rd->next; + else st->dimensions = rd->next; + rd->next = NULL; rrddim_index_del(st, rd); @@ -709,8 +711,8 @@ RRDSET *rrdset_find_bytype(const char *type, const char *id) strncpy(buf, type, RRD_ID_LENGTH_MAX - 1); buf[RRD_ID_LENGTH_MAX - 1] = '\0'; strcat(buf, "."); - int len = strlen(buf); - strncpy(&buf[len], id, RRD_ID_LENGTH_MAX - len); + int len = (int) strlen(buf); + strncpy(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len)); buf[RRD_ID_LENGTH_MAX] = '\0'; return(rrdset_find(buf)); @@ -850,8 +852,8 @@ unsigned long long rrdset_done(RRDSET *st) // it is not the first entry // calculate the proper last_collected_time, using usec_since_last_update unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update; - st->last_collected_time.tv_sec = ut / 1000000ULL; - st->last_collected_time.tv_usec = ut % 1000000ULL; + st->last_collected_time.tv_sec = (__time_t) (ut / 1000000ULL); + st->last_collected_time.tv_usec = (__suseconds_t) (ut % 1000000ULL); } // if this set has not been updated in the past @@ -860,8 +862,8 @@ unsigned long long rrdset_done(RRDSET *st) // it has never been updated before // set a fake last_updated, in the past using usec_since_last_update unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = ut / 1000000ULL; - st->last_updated.tv_usec = ut % 1000000ULL; + st->last_updated.tv_sec = (__time_t) (ut / 1000000ULL); + st->last_updated.tv_usec = (__suseconds_t) (ut % 1000000ULL); // the first entry should not be stored store_this_entry = 0; @@ -879,8 +881,8 @@ unsigned long long rrdset_done(RRDSET *st) gettimeofday(&st->last_collected_time, NULL); unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = ut / 1000000ULL; - st->last_updated.tv_usec = ut % 1000000ULL; + st->last_updated.tv_sec = (__time_t) (ut / 1000000ULL); + st->last_updated.tv_usec = (__suseconds_t) (ut % 1000000ULL); // the first entry should not be stored store_this_entry = 0; @@ -1049,7 +1051,7 @@ unsigned long long rrdset_done(RRDSET *st) // it is now time to interpolate values on a second boundary unsigned long long first_ut = last_ut; - int iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL); + long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL); for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) { #ifdef NETDATA_INTERNAL_CHECKS @@ -1061,7 +1063,7 @@ unsigned long long rrdset_done(RRDSET *st) debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0); } - st->last_updated.tv_sec = next_ut / 1000000ULL; + st->last_updated.tv_sec = (__time_t) (next_ut / 1000000ULL); st->last_updated.tv_usec = 0; for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) { @@ -1084,8 +1086,8 @@ unsigned long long rrdset_done(RRDSET *st) , st->id, rd->name , new_value , rd->calculated_value - , (unsigned long long)(next_ut - last_ut) - , (unsigned long long)(now_ut - last_ut) + , (next_ut - last_ut) + , (now_ut - last_ut) ); rd->calculated_value -= new_value; diff --git a/src/rrd.h b/src/rrd.h index ab9935ad..b482d3e4 100755 --- a/src/rrd.h +++ b/src/rrd.h @@ -1,5 +1,6 @@ #include #include +#include #include "avl.h" #include "storage_number.h" diff --git a/src/rrd2json.c b/src/rrd2json.c index 1e718b48..ce5d26e3 100755 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -2,9 +2,9 @@ #include #endif #include -#include #include #include +#include #include "log.h" #include "common.h" @@ -270,8 +270,8 @@ typedef struct rrdresult { RRDSET *st; // the chart this result refers to int d; // the number of dimensions - int n; // the number of values in the arrays - int rows; // the number of rows used + long n; // the number of values in the arrays + long rows; // the number of rows used uint8_t *od; // the options for the dimensions @@ -279,10 +279,10 @@ typedef struct rrdresult { calculated_number *v; // array n x d values uint8_t *o; // array n x d options - int c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows ) + long c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows ) - int group; // how many collected values were grouped for each row - int update_every; // what is the suggested update frequency in seconds + long group; // how many collected values were grouped for each row + long update_every; // what is the suggested update frequency in seconds calculated_number min; calculated_number max; @@ -449,7 +449,7 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options) void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) { - int rows = rrdr_rows(r); + long rows = rrdr_rows(r); long c, i; RRDDIM *rd; @@ -629,7 +629,7 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable) { //info("RRD2JSON(): %s: BEGIN", r->st->id); - int row_annotations = 0, dates = JSON_DATES_JS, dates_with_new = 0; + int row_annotations = 0, dates, dates_with_new = 0; char kq[2] = "", // key quote sq[2] = "", // string quote pre_label[101] = "", // before each label @@ -1051,7 +1051,7 @@ inline void rrdr_done(RRDR *r) r->c = 0; } -static RRDR *rrdr_create(RRDSET *st, int n) +static RRDR *rrdr_create(RRDSET *st, long n) { if(unlikely(!st)) { error("NULL value given!"); @@ -1546,7 +1546,7 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long return 200; } -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) +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) { int c; pthread_rwlock_rdlock(&st->rwlock); diff --git a/src/rrd2json.h b/src/rrd2json.h index 353145ab..87ba205a 100755 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -57,7 +57,7 @@ extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb); extern void rrd_stats_all_json(BUFFER *wb); -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); +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); 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); diff --git a/src/storage_number.c b/src/storage_number.c index 2936c24b..225cf034 100755 --- a/src/storage_number.c +++ b/src/storage_number.c @@ -76,7 +76,7 @@ storage_number pack_storage_number(calculated_number value, uint32_t flags) #ifdef STORAGE_WITH_MATH // without this there are rounding problems // example: 0.9 becomes 0.89 - r += lrint(n); + r += lrint((double) n); #else r += (storage_number)n; #endif @@ -153,7 +153,7 @@ int print_calculated_number(char *str, calculated_number value) #ifdef STORAGE_WITH_MATH // without llrint() there are rounding problems // for example 0.9 becomes 0.89 - unsigned long long uvalue = llrint(value * (calculated_number)100000); + unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000); #else unsigned long long uvalue = value * (calculated_number)100000; #endif @@ -202,5 +202,5 @@ int print_calculated_number(char *str, calculated_number value) else wstr[1] = '.'; // return the buffer length - return ( (wstr - str) + 2 + decimal ); + return (int) ((wstr - str) + 2 + decimal ); } diff --git a/src/web_buffer.c b/src/web_buffer.c index c2e6b03c..7a169eb4 100755 --- a/src/web_buffer.c +++ b/src/web_buffer.c @@ -52,7 +52,7 @@ void buffer_reset(BUFFER *wb) const char *buffer_tostring(BUFFER *wb) { - buffer_need_bytes(wb, 1); + buffer_need_bytes(wb, (size_t)1); wb->buffer[wb->len] = '\0'; buffer_overflow_check(wb); @@ -77,8 +77,7 @@ void buffer_strcat(BUFFER *wb, const char *txt) { if(unlikely(!txt || !*txt)) return; - if(wb->size - wb->len < 512) - buffer_need_bytes(wb, 512); + buffer_need_bytes(wb, (size_t)(1)); char *s = &wb->buffer[wb->len], *end = &wb->buffer[wb->size]; long len = wb->len; @@ -100,7 +99,7 @@ void buffer_strcat(BUFFER *wb, const char *txt) else { // terminate the string // without increasing the length - buffer_need_bytes(wb, 1); + buffer_need_bytes(wb, (size_t)1); wb->buffer[wb->len] = '\0'; } } @@ -126,8 +125,9 @@ void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args) { if(unlikely(!fmt || !*fmt)) return; + buffer_need_bytes(wb, 1); + size_t len = wb->size - wb->len; - if(unlikely(!len)) return; wb->len += vsnprintf(&wb->buffer[wb->len], len, fmt, args); @@ -140,20 +140,13 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...) { if(unlikely(!fmt || !*fmt)) return; - if(unlikely(wb->len > wb->size)) { - error("web_buffer_sprintf(): already overflown length %ld, size = %ld", wb->len, wb->size); - } - -// if(wb->size - wb->len < 512) -// web_buffer_need_bytes(wb, 512); + buffer_need_bytes(wb, 1); size_t len = wb->size - wb->len, wrote; - buffer_need_bytes(wb, len); - va_list args; va_start(args, fmt); - wrote = vsnprintf(&wb->buffer[wb->len], len, fmt, args); + wrote = (size_t) vsnprintf(&wb->buffer[wb->len], len, fmt, args); va_end(args); if(unlikely(wrote >= len)) { @@ -207,25 +200,25 @@ void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minu b[i++]='t'; b[i++]='e'; b[i++]='('; - b[i++]= 48 + year / 1000; year -= (year / 1000) * 1000; - b[i++]= 48 + year / 100; year -= (year / 100) * 100; - b[i++]= 48 + year / 10; - b[i++]= 48 + year % 10; + b[i++]= (char) (48 + year / 1000); year -= (year / 1000) * 1000; + b[i++]= (char) (48 + year / 100); year -= (year / 100) * 100; + b[i++]= (char) (48 + year / 10); + b[i++]= (char) (48 + year % 10); b[i++]=','; - b[i]= 48 + month / 10; if(b[i] != '0') i++; - b[i++]= 48 + month % 10; + b[i]= (char) (48 + month / 10); if(b[i] != '0') i++; + b[i++]= (char) (48 + month % 10); b[i++]=','; - b[i]= 48 + day / 10; if(b[i] != '0') i++; - b[i++]= 48 + day % 10; + b[i]= (char) (48 + day / 10); if(b[i] != '0') i++; + b[i++]= (char) (48 + day % 10); b[i++]=','; - b[i]= 48 + hours / 10; if(b[i] != '0') i++; - b[i++]= 48 + hours % 10; + b[i]= (char) (48 + hours / 10); if(b[i] != '0') i++; + b[i++]= (char) (48 + hours % 10); b[i++]=','; - b[i]= 48 + minutes / 10; if(b[i] != '0') i++; - b[i++]= 48 + minutes % 10; + b[i]= (char) (48 + minutes / 10); if(b[i] != '0') i++; + b[i++]= (char) (48 + minutes % 10); b[i++]=','; - b[i]= 48 + seconds / 10; if(b[i] != '0') i++; - b[i++]= 48 + seconds % 10; + b[i]= (char) (48 + seconds / 10); if(b[i] != '0') i++; + b[i++]= (char) (48 + seconds % 10); b[i++]=')'; b[i]='\0'; @@ -248,25 +241,25 @@ void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minute char *b = &wb->buffer[wb->len]; int i = 0; - b[i++]= 48 + year / 1000; year -= (year / 1000) * 1000; - b[i++]= 48 + year / 100; year -= (year / 100) * 100; - b[i++]= 48 + year / 10; - b[i++]= 48 + year % 10; + b[i++]= (char) (48 + year / 1000); year -= (year / 1000) * 1000; + b[i++]= (char) (48 + year / 100); year -= (year / 100) * 100; + b[i++]= (char) (48 + year / 10); + b[i++]= (char) (48 + year % 10); b[i++]='-'; - b[i++]= 48 + month / 10; - b[i++]= 48 + month % 10; + b[i++]= (char) (48 + month / 10); + b[i++]= (char) (48 + month % 10); b[i++]='-'; - b[i++]= 48 + day / 10; - b[i++]= 48 + day % 10; + b[i++]= (char) (48 + day / 10); + b[i++]= (char) (48 + day % 10); b[i++]=' '; - b[i++]= 48 + hours / 10; - b[i++]= 48 + hours % 10; + b[i++]= (char) (48 + hours / 10); + b[i++]= (char) (48 + hours % 10); b[i++]=':'; - b[i++]= 48 + minutes / 10; - b[i++]= 48 + minutes % 10; + b[i++]= (char) (48 + minutes / 10); + b[i++]= (char) (48 + minutes % 10); b[i++]=':'; - b[i++]= 48 + seconds / 10; - b[i++]= 48 + seconds % 10; + b[i++]= (char) (48 + seconds / 10); + b[i++]= (char) (48 + seconds % 10); b[i]='\0'; wb->len += i; @@ -313,15 +306,15 @@ void buffer_free(BUFFER *b) free(b); } -void buffer_increase(BUFFER *b, long free_size_required) +void buffer_increase(BUFFER *b, size_t free_size_required) { buffer_overflow_check(b); - long left = b->size - b->len; + size_t left = b->size - b->len; if(left >= free_size_required) return; - long increase = free_size_required - left; + size_t increase = free_size_required - left; if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP; debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase); diff --git a/src/web_buffer.h b/src/web_buffer.h index c282727a..4f01f199 100755 --- a/src/web_buffer.h +++ b/src/web_buffer.h @@ -8,8 +8,8 @@ #define WEB_DATA_LENGTH_INCREASE_STEP 16384 typedef struct web_buffer { - long size; // allocation size of buffer - long len; // current data length in buffer + size_t size; // allocation size of buffer + size_t len; // current data length in buffer char *buffer; // the buffer int contenttype; time_t date; // the date this content has been generated @@ -35,7 +35,7 @@ typedef struct web_buffer { #define buffer_strlen(wb) ((wb)->len) extern const char *buffer_tostring(BUFFER *wb); -#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) +#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) #define buffer_flush(wb) wb->buffer[wb->len = 0] = '\0' 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 extern BUFFER *buffer_create(long size); extern void buffer_free(BUFFER *b); -extern void buffer_increase(BUFFER *b, long free_size_required); +extern void buffer_increase(BUFFER *b, size_t free_size_required); extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...); extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); diff --git a/src/web_client.c b/src/web_client.c index f294e4bc..5f5ff78a 100755 --- a/src/web_client.c +++ b/src/web_client.c @@ -436,7 +436,7 @@ uint32_t web_client_api_request_v1_data_options(char *o) return ret; } -int web_client_api_request_v1_data_format(char *name) +uint32_t web_client_api_request_v1_data_format(char *name) { if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable return DATASOURCE_DATATABLE_JSON; @@ -471,7 +471,7 @@ int web_client_api_request_v1_data_format(char *name) return DATASOURCE_JSON; } -int web_client_api_request_v1_data_google_format(char *name) +uint32_t web_client_api_request_v1_data_google_format(char *name) { if(!strcmp(name, "json")) return DATASOURCE_DATATABLE_JSONP; @@ -579,7 +579,8 @@ int web_client_api_request_v1_data(struct web_client *w, char *url) , *after_str = NULL , *points_str = NULL; - int format = DATASOURCE_JSON, group = GROUP_MAX; + int group = GROUP_MAX; + uint32_t format = DATASOURCE_JSON; uint32_t options = 0x00000000; while(url) { @@ -842,7 +843,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type char *google_out = "json"; char *google_responseHandler = "google.visualization.Query.setResponse"; char *google_outFileName = NULL; - unsigned long last_timestamp_in_data = 0; + time_t last_timestamp_in_data = 0; if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) { 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 } 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); - unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero); + time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero); if(datasource_type == DATASOURCE_DATATABLE_JSONP) { if(timestamp_in_data > last_timestamp_in_data) @@ -969,7 +970,7 @@ cleanup: void web_client_process(struct web_client *w) { int code = 500; - int bytes; + ssize_t bytes; w->wait_receive = 0; @@ -1179,7 +1180,7 @@ void web_client_process(struct web_client *w) { // prepare the HTTP response header debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code); - char *content_type_string = ""; + char *content_type_string; switch(w->response.data->contenttype) { case CT_TEXT_HTML: content_type_string = "text/html; charset=utf-8"; @@ -1243,7 +1244,7 @@ void web_client_process(struct web_client *w) { break; } - char *code_msg = ""; + char *code_msg; switch(code) { case 200: code_msg = "OK"; @@ -1308,7 +1309,7 @@ void web_client_process(struct web_client *w) { if(!w->response.zoutput && (w->response.data->len || w->response.rlen)) buffer_sprintf(w->response.header_output, "Content-Length: %ld\r\n" - , w->response.data->len?w->response.data->len:w->response.rlen + , w->response.data->len? w->response.data->len: w->response.rlen ); else if(!w->response.zoutput) w->keepalive = 0; // content-length is required for keep-alive @@ -1335,7 +1336,7 @@ void web_client_process(struct web_client *w) { ); bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0); - if(bytes != buffer_strlen(w->response.header_output)) + if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes)." , w->id , buffer_strlen(w->response.header_output) @@ -1386,12 +1387,12 @@ void web_client_process(struct web_client *w) { } } -long web_client_send_chunk_header(struct web_client *w, int len) +long web_client_send_chunk_header(struct web_client *w, long len) { debug(D_DEFLATE, "%llu: OPEN CHUNK of %d bytes (hex: %x).", w->id, len, len); char buf[1024]; - sprintf(buf, "%X\r\n", len); - int bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT); + sprintf(buf, "%lX\r\n", len); + ssize_t bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT); if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk header %d bytes.", w->id, bytes); 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) { //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id); - int bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT); + ssize_t bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT); if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes); 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) { //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id); - int bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT); + ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT); if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes); 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) // give the compressor all the data not passed through the compressor yet if(w->response.data->len > w->response.sent) { w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent]; - w->response.zstream.avail_in = (w->response.data->len - w->response.sent); + w->response.zstream.avail_in = (uInt) (w->response.data->len - w->response.sent); } // reset the compressor output buffer @@ -1516,7 +1517,7 @@ long web_client_send_deflate(struct web_client *w) t += web_client_send_chunk_header(w, w->response.zhave); } - len = send(w->ofd, &w->response.zbuffer[w->response.zsent], w->response.zhave - w->response.zsent, MSG_DONTWAIT); + len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT); if(len > 0) { w->response.zsent += len; if(t > 0) len += t; @@ -1584,12 +1585,12 @@ long web_client_receive(struct web_client *w) long bytes; if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) - bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (left-1)); + bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1)); else - bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], left-1, MSG_DONTWAIT); + bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT); if(likely(bytes > 0)) { - int old = w->response.data->len; + size_t old = w->response.data->len; w->response.data->len += bytes; w->response.data->buffer[w->response.data->len] = '\0'; diff --git a/src/web_client.h b/src/web_client.h index a32fce31..9762dcc3 100755 --- a/src/web_client.h +++ b/src/web_client.h @@ -33,8 +33,8 @@ struct response { int code; // the HTTP response code - long rlen; // if non-zero, the excepted size of ifd (input) - long sent; // current data length sent to output + size_t rlen; // if non-zero, the excepted size of ifd (input) + size_t sent; // current data length sent to output int zoutput; // if set to 1, web_client_send() will send compressed data #ifdef NETDATA_WITH_ZLIB diff --git a/src/web_server.c b/src/web_server.c index fbfc034a..cae94aca 100755 --- a/src/web_server.c +++ b/src/web_server.c @@ -49,7 +49,7 @@ static void log_allocations(void) int create_listen_socket4(int port, int listen_backlog) { - int sock = -1; + int sock; int sockopt = 1; struct sockaddr_in name; @@ -104,7 +104,7 @@ int create_listen_socket6(int port, int listen_backlog) memset(&name, 0, sizeof(struct sockaddr_in6)); name.sin6_family = AF_INET6; - name.sin6_port = htons (port); + name.sin6_port = htons ((uint16_t) port); name.sin6_addr = in6addr_any; name.sin6_scope_id = 0; @@ -151,7 +151,7 @@ void *socket_listen_main(void *ptr) if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - web_client_timeout = config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS); + web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS); web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip); if(listen_fd < 0) fatal("LISTENER: Listen socket is not ready."); diff --git a/web/dashboard.css b/web/dashboard.css index 36b08960..6cb3a582 100755 --- a/web/dashboard.css +++ b/web/dashboard.css @@ -50,35 +50,32 @@ html { color: #DDDDDD; text-align: center; overflow: hidden; - z-index: 11; + z-index: 20; padding: 0px; margin: 0px; } .netdata-message { - display: none; + display: inline-block; + text-align: left; + vertical-align: top; + font-weight: bold; font-size: x-small; width: 100%; - height: calc(100% - 10px); /* for the resize handler */ + height: 100%; overflow: hidden; background: inherit; - z-index: 10000; + z-index: 0; } -.netdata-chart-is-loading { +.netdata-message.hidden { display: none; - font-size: x-small; - width: 100%; - overflow: hidden; - background: white; - z-index: 10000; -} - -.netdata-error-message { - background: #EEEEEE; } -.netdata-info-message { +.netdata-message.icon { + color: #FAFAFA; + text-align: center; + vertical-align: middle; } .netdata-chart-legend { @@ -256,6 +253,7 @@ html { overflow: hidden; width: 100%; height: 100%; + z-index: 5; /* width and height is calculated (depends on the appearance of the legend) */ } @@ -269,6 +267,7 @@ html { margin-right: 140px; /* --legend-width */ width: calc(100% - 140px); /* --legend-width */ height: 100%; + z-index: 5; flex-grow: 1; /* width and height is calculated (depends on the appearance of the legend) */ diff --git a/web/dashboard.js b/web/dashboard.js index 5b5e0b4b..ea084348 100755 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -354,7 +354,7 @@ var a = i.split('.'); if(a[0] === 'options') { - if(a[1] === 'options.setOptionCallback') continue; + if(a[1] === 'setOptionCallback') continue; if(typeof NETDATA.localStorage.default[i] === 'undefined') continue; if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue; @@ -386,9 +386,9 @@ // hidden all the not-visible charts // using this little function we try to switch // the charts back to visible quickly - var targets = NETDATA.options.targets; - var len = targets.length; - while(len--) targets[len].isVisible(); + //var targets = NETDATA.options.targets; + //var len = targets.length; + //while(len--) targets[len].isVisible(); } // ---------------------------------------------------------------------------------------------------------------- @@ -581,6 +581,7 @@ // dimensions selection // FIXME + // move color assignment to dimensions, here dimensionStatus = function(parent, label, name_div, value_div, color) { this.enabled = false; @@ -777,17 +778,45 @@ // ---------------------------------------------------------------------------------------------------------------- - // Our state object, where all per-chart values are stored + // global selection sync - resizeCallback = function(state) { - this.state = state; - this.resize = function(height) { - this.state + NETDATA.globalSelectionSync = { + state: null, + dont_sync_before: 0, + last_t: 0, + slaves: [], + + stop: function() { + if(this.state !== null) + this.state.globalSelectionSyncStop(); + }, + + delay: function() { + if(this.state !== null) { + this.state.globalSelectionSyncDelay(); + } } - } + }; + + // ---------------------------------------------------------------------------------------------------------------- + // Our state object, where all per-chart values are stored chartState = function(element) { var self = $(element); + this.element = element; + + // IMPORTANT: + // all private functions should use 'that', instead of 'this' + var that = this; + + /* error() - private + * show an error instead of the chart + */ + var error = function(msg) { + that.element.innerHTML = that.id + ': ' + msg; + that.enabled = false; + that.current = that.pan; + } // GUID - a unique identifier for the chart this.uuid = NETDATA.guid(); @@ -803,1725 +832,1667 @@ this.height = self.data('height') || NETDATA.chartDefaults.height; if(this.settings_id !== null) { - var me = this; this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) { - me.resizeAndRedrawChart(height); + // this is the callback that will be called + // if and when the user resets all localStorage variables + // to their defaults + + resizeChartToHeight(height); }); } - $.extend(this, { - // string - the netdata server URL, without any path - host: self.data('host') || NETDATA.chartDefaults.host, - - // string - the grouping method requested by the user - method: self.data('method') || NETDATA.chartDefaults.method, - - // the time-range requested by the user - after: self.data('after') || NETDATA.chartDefaults.after, - before: self.data('before') || NETDATA.chartDefaults.before, - - // the pixels per point requested by the user - pixels_per_point: self.data('pixels-per-point') || 1, - points: self.data('points') || null, - - // the dimensions requested by the user - dimensions: self.data('dimensions') || null, - - // the chart library requested by the user - library_name: self.data('chart-library') || NETDATA.chartDefaults.library, - library: null, // object - the chart library used - - colors: null, - colors_assigned: {}, - colors_available: null, - - element: element, // the element already created by the user - element_message: null, - element_loading: null, - element_chart: null, // the element with the chart - element_chart_id: null, - element_legend: null, // the element with the legend of the chart (if created by us) - element_legend_id: null, - element_legend_childs: { - hidden: null, - title_date: null, - title_time: null, - title_units: null, - nano: null, - nano_options: null, - series: null - }, - - chart_url: null, // string - the url to download chart info - chart: null, // object - the chart as downloaded from the server - - validated: false, // boolean - has the chart been validated? - enabled: true, // boolean - is the chart enabled for refresh? - paused: false, // boolean - is the chart paused for any reason? - selected: false, // boolean - is the chart shown a selection? - debug: false, // boolean - console.log() debug info about this chart + // string - the netdata server URL, without any path + this.host = self.data('host') || NETDATA.chartDefaults.host; - dom_created: false, // boolean - is the DOM for the chart created? - chart_created: false, // boolean - is the library.create() been called? - - updates_counter: 0, // numeric - the number of refreshes made so far - updates_since_last_creation: 0, + // make sure the host does not end with / + // all netdata API requests use absolute paths + while(this.host.slice(-1) === '/') + this.host = this.host.substring(0, this.host.length - 1); - tm: { - last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart + // string - the grouping method requested by the user + this.method = self.data('method') || NETDATA.chartDefaults.method; - last_updated: 0, // the timestamp the chart last updated with data + // the time-range requested by the user + this.after = self.data('after') || NETDATA.chartDefaults.after; + this.before = self.data('before') || NETDATA.chartDefaults.before; - pan_and_zoom_seq: 0, // the sequence number of the global synchronization - // between chart. - // Used with NETDATA.globalPanAndZoom.seq + // the pixels per point requested by the user + this.pixels_per_point = self.data('pixels-per-point') || 1; + this.points = self.data('points') || null; - last_visible_check: 0, // the time we last checked if it is visible + // the dimensions requested by the user + this.dimensions = self.data('dimensions') || null; - last_resized: 0, // the time the chart was resized - last_hidden: 0, // the time the chart was hidden - last_unhidden: 0, // the time the chart was unhidden + // the chart library requested by the user + this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library; - last_autorefreshed: 0 // the time the chart was last refreshed - }, + // object - the chart library used + this.library = null; - data: null, // the last data as downloaded from the netdata server - data_url: 'invalid://', // string - the last url used to update the chart - data_points: 0, // number - the number of points returned from netdata - data_after: 0, // milliseconds - the first timestamp of the data - data_before: 0, // milliseconds - the last timestamp of the data - data_update_every: 0, // milliseconds - the frequency to update the data - netdata_first: 0, // milliseconds - the first timestamp in netdata - netdata_last: 0, // milliseconds - the last timestamp in netdata - - dimensions_visibility: new dimensionsVisibility(this), - - current: null, // auto, pan, zoom - // this is a pointer to one of the sub-classes below - - auto: { - name: 'auto', - autorefresh: true, - force_update_at: 0, // the timestamp to force the update at - force_before_ms: null, - force_after_ms: null, - requested_before_ms: null, - requested_after_ms: null, - }, - pan: { - name: 'pan', - autorefresh: false, - force_update_at: 0, // the timestamp to force the update at - force_before_ms: null, - force_after_ms: null, - requested_before_ms: null, - requested_after_ms: null, - }, - zoom: { - name: 'zoom', - autorefresh: false, - force_update_at: 0, // the timestamp to force the update at - force_before_ms: null, - force_after_ms: null, - requested_before_ms: null, - requested_after_ms: null, - }, + // color management + this.colors = null; + this.colors_assigned = {}; + this.colors_available = null; - refresh_dt_ms: 0, // milliseconds - the time the last refresh took - refresh_dt_element_name: self.data('dt-element-name') || null, // string - the element to print refresh_dt_ms - refresh_dt_element: null - }); + // the element already created by the user + this.element_message = null; - this.init(); - } + // the element with the chart + this.element_chart = null; - // ---------------------------------------------------------------------------------------------------------------- - // Chart Resize - - // this is actual chart resize algorithm - // it will: - // - resize the entire container - // - update the internal states - // - resize the chart as the div changes height - // - update the scrollbar of the legend - chartState.prototype.resizeAndRedrawChart = function(h) { - // console.log(h); - this.element.style.height = h; - - if(this.settings_id !== null) - NETDATA.localStorageSet('chart_heights.' + this.settings_id, h); - - var now = new Date().getTime(); - NETDATA.options.last_page_scroll = now; - NETDATA.options.last_resized = now; - NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing; - - if(typeof this.library.resize === 'function' && this.element_chart !== null) - this.library.resize(this); - - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) - $(this.element_legend_childs.nano).nanoScroller(); - }; + // the element with the legend of the chart (if created by us) + this.element_legend = null; + this.element_legend_childs = { + hidden: null, + title_date: null, + title_time: null, + title_units: null, + nano: null, + nano_options: null, + series: null + }; - chartState.prototype.resizeHandler = function(e) { - e.preventDefault(); + this.chart_url = null; // string - the url to download chart info + this.chart = null; // object - the chart as downloaded from the server + + this.validated = false; // boolean - has the chart been validated? + this.enabled = true; // boolean - is the chart enabled for refresh? + this.paused = false; // boolean - is the chart paused for any reason? + this.selected = false; // boolean - is the chart shown a selection? + this.debug = false; // boolean - console.log() debug info about this chart + + this.netdata_first = 0; // milliseconds - the first timestamp in netdata + this.netdata_last = 0; // milliseconds - the last timestamp in netdata + this.requested_after = null; // milliseconds - the timestamp of the request after param + this.requested_before = null; // milliseconds - the timestamp of the request before param + + this.auto = { + name: 'auto', + autorefresh: true, + force_update_at: 0, // the timestamp to force the update at + force_before_ms: null, + force_after_ms: null + }; + this.pan = { + name: 'pan', + autorefresh: false, + force_update_at: 0, // the timestamp to force the update at + force_before_ms: null, + force_after_ms: null + }; + this.zoom = { + name: 'zoom', + autorefresh: false, + force_update_at: 0, // the timestamp to force the update at + force_before_ms: null, + force_after_ms: null + }; - if(typeof this.event_resize === 'undefined' - || this.event_resize.chart_original_w === 'undefined' - || this.event_resize.chart_original_h === 'undefined') - this.event_resize = { - chart_original_w: this.element.clientWidth, - chart_original_h: this.element.clientHeight, - last: 0 - }; + // this is a pointer to one of the sub-classes below + // auto, pan, zoom + this.current = this.auto; - if(e.type === 'touchstart') { - this.event_resize.mouse_start_x = e.touches.item(0).pageX; - this.event_resize.mouse_start_y = e.touches.item(0).pageY; + // check the requested library is available + // we don't initialize it here - it will be initialized when + // this chart will be first used + if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') { + NETDATA.error(402, that.library_name); + error('chart library "' + that.library_name + '" is not found'); + return; } - else { - this.event_resize.mouse_start_x = e.clientX; - this.event_resize.mouse_start_y = e.clientY; + else if(NETDATA.chartLibraries[that.library_name].enabled === false) { + NETDATA.error(403, that.library_name); + error('chart library "' + that.library_name + '" is not enabled'); + return; } + else + that.library = NETDATA.chartLibraries[that.library_name]; - this.event_resize.chart_start_w = this.element.clientWidth; - this.event_resize.chart_start_h = this.element.clientHeight; - this.event_resize.chart_last_w = this.element.clientWidth; - this.event_resize.chart_last_h = this.element.clientHeight; + // milliseconds - the time the last refresh took + this.refresh_dt_ms = 0; - var now = new Date().getTime(); - if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) { - // double click / double tap event + // if we need to report the rendering speed + // find the element that needs to be updated + var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms - // the optimal height of the chart - // showing the entire legend - var optimal = this.event_resize.chart_last_h - + this.element_legend_childs.content.scrollHeight - - this.element_legend_childs.content.clientHeight; + if(refresh_dt_element_name !== null) + this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; + else + this.refresh_dt_element = null; - // if we are not optimal, be optimal - if(this.event_resize.chart_last_h != optimal) - this.resizeAndRedrawChart(optimal.toString() + 'px'); + this.dimensions_visibility = new dimensionsVisibility(this); - // else if we do not have the original height - // reset to the original height - else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h) - this.resizeAndRedrawChart(this.event_resize.chart_original_h.toString() + 'px'); - } - else { - this.event_resize.last = now; - var self = this; + // ============================================================================================================ + // PRIVATE FUNCTIONS - // process movement event - document.onmousemove = - document.ontouchmove = - this.element_legend_childs.resize_handler.onmousemove = - this.element_legend_childs.resize_handler.ontouchmove = - function(e) { - var y = null; - - switch(e.type) { - case 'mousemove': y = e.clientY; break; - case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break; - } + var createDOM = function() { + if(that.enabled == false) return; - if(y !== null) { - var newH = self.event_resize.chart_start_h + y - self.event_resize.mouse_start_y; + if(that.element_message !== null) that.element_message.innerHTML = ''; + if(that.element_legend !== null) that.element_legend.innerHTML = ''; + if(that.element_chart !== null) that.element_chart.innerHTML = ''; - if(newH >= 70 && newH !== self.event_resize.chart_last_h) { - self.resizeAndRedrawChart(newH.toString() + 'px'); - self.event_resize.chart_last_h = newH; - } - } - }; + that.element.innerHTML = ''; - // process end event - document.onmouseup = - document.ontouchend = - this.element_legend_childs.resize_handler.onmouseup = - this.element_legend_childs.resize_handler.ontouchend = - function(e) { - // remove all the hooks - document.onmouseup = - document.onmousemove = - document.ontouchmove = - document.ontouchend = - self.element_legend_childs.resize_handler.onmousemove = - self.element_legend_childs.resize_handler.ontouchmove = - self.element_legend_childs.resize_handler.onmouseout = - self.element_legend_childs.resize_handler.onmouseup = - self.element_legend_childs.resize_handler.ontouchend = - null; - - // allow auto-refreshes - NETDATA.options.auto_refresher_stop_until = 0; - }; - } - } + that.element_message = document.createElement('div'); + that.element_message.className = ' netdata-message hidden'; + that.element.appendChild(that.element_message); - - // ---------------------------------------------------------------------------------------------------------------- - // global selection sync + that.element_chart = document.createElement('div'); + that.element_chart.id = that.library_name + '-' + that.uuid + '-chart'; + that.element.appendChild(that.element_chart); - NETDATA.globalSelectionSync = { - state: null, - dont_sync_before: 0, - slaves: [] - }; + if(that.hasLegend() === true) { + that.element.className = "netdata-container-with-legend"; + that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right'; - // prevent to global selection sync for some time - chartState.prototype.globalSelectionSyncDelay = function(ms) { - if(NETDATA.options.current.sync_selection === false) - return; + that.element_legend = document.createElement('div'); + that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend'; + that.element.appendChild(that.element_legend); + } + else { + that.element.className = "netdata-container"; + that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart'; - if(typeof ms === 'number') - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms; - else - NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay; - } + that.element_legend = null; + } + that.element_legend_childs.series = null; - // can we globally apply selection sync? - chartState.prototype.globalSelectionSyncAbility = function() { - if(NETDATA.options.current.sync_selection === false) - return false; + if(that.width !== 0) + $(that.element).css('width', that.width); - if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) return false; - return true; - } + if(that.height !== 0) + $(that.element).css('height', that.height); - chartState.prototype.globalSelectionSyncIsMaster = function() { - if(NETDATA.globalSelectionSync.state === this) - return true; - else - return false; - } + if(NETDATA.chartDefaults.min_width !== null) + $(that.element).css('min-width', NETDATA.chartDefaults.min_width); - // this chart is the master of the global selection sync - chartState.prototype.globalSelectionSyncBeMaster = function() { - // am I the master? - if(this.globalSelectionSyncIsMaster()) { - if(this.debug === true) - this.log('sync: I am the master already.'); + that.tm.last_dom_created = new Date().getTime(); - return; + showLoading(); } - if(NETDATA.globalSelectionSync.state) { - if(this.debug === true) - this.log('sync: I am not the sync master. Resetting global sync.'); + /* init() private + * initialize state viariables + * destroy all (possibly) created state elements + * create the basic DOM for a chart + */ + var init = function() { + if(that.enabled == false) return; - this.globalSelectionSyncStop(); - } + that.paused = false; + that.selected = false; - // become the master - if(this.debug === true) - this.log('sync: becoming sync master.'); + that.chart_created = false; // boolean - is the library.create() been called? + that.updates_counter = 0; // numeric - the number of refreshes made so far + that.updates_since_last_creation = 0; - this.selected = true; - NETDATA.globalSelectionSync.state = this; + that.tm = { + last_initialized: 0, // milliseconds - the timestamp it was last initialized + last_dom_created: 0, // milliseconds - the timestamp its DOM was last created + last_mode_switch: 0, // milliseconds - the timestamp it switched modes - // find the all slaves - var targets = NETDATA.options.targets; - var len = targets.length; - while(len--) { - st = targets[len]; + last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart + last_updated: 0, // the timestamp the chart last updated with data + pan_and_zoom_seq: 0, // the sequence number of the global synchronization + // between chart. + // Used with NETDATA.globalPanAndZoom.seq + last_visible_check: 0, // the time we last checked if it is visible + last_resized: 0, // the time the chart was resized + last_hidden: 0, // the time the chart was hidden + last_unhidden: 0, // the time the chart was unhidden + last_autorefreshed: 0 // the time the chart was last refreshed + }, - if(st === this) { - if(this.debug === true) - st.log('sync: not adding me to sync'); - } - else if(st.globalSelectionSyncIsEligible()) { - if(this.debug === true) - st.log('sync: adding to sync as slave'); + that.data = null; // the last data as downloaded from the netdata server + that.data_url = 'invalid://'; // string - the last url used to update the chart + that.data_points = 0; // number - the number of points returned from netdata + that.data_after = 0; // milliseconds - the first timestamp of the data + that.data_before = 0; // milliseconds - the last timestamp of the data + that.data_update_every = 0; // milliseconds - the frequency to update the data - st.globalSelectionSyncBeSlave(); - } + that.tm.last_initialized = new Date().getTime(); + createDOM(); + + that.setMode('auto'); } - // this.globalSelectionSyncDelay(100); - } + var maxMessageFontSize = function() { + // normally we want a font size, as tall as the element + var h = that.element_message.clientHeight; - // can the chart participate to the global selection sync as a slave? - chartState.prototype.globalSelectionSyncIsEligible = function() { - if(this.enabled === true - && this.library !== null - && typeof this.library.setSelection === 'function' - && this.isVisible() - && this.dom_created === true - && this.chart_created === true) - return true; + // but give it some air, 20% let's say, or 5 pixels min + var lost = Math.max(h * 0.2, 5); + h -= lost; - return false; - } + // center the text, verically + var paddingTop = (lost - 5) / 2; - // this chart is a slave of the global selection sync - chartState.prototype.globalSelectionSyncBeSlave = function() { - if(NETDATA.globalSelectionSync.state !== this) - NETDATA.globalSelectionSync.slaves.push(this); - } + // but check the width too + // it should fit 10 characters in it + var w = that.element_message.clientWidth / 10; + if(h > w) { + paddingTop += (h - w) / 2; + h = w; + } - // sync all the visible charts to the given time - // this is to be called from the chart libraries - chartState.prototype.globalSelectionSync = function(t) { - if(this.globalSelectionSyncAbility() === false) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); + // and don't make it too huge + // 5% of the screen size is good + if(h > screen.height / 20) { + paddingTop += (h - (screen.height / 20)) / 2; + h = screen.height / 20; + } - return; + // set it + that.element_message.style.fontSize = h.toString() + 'px'; + that.element_message.style.paddingTop = paddingTop.toString() + 'px'; } - if(this.globalSelectionSyncIsMaster() === false) { - if(this.debug === true) - this.log('sync: trying to be sync master.'); - - this.globalSelectionSyncBeMaster(); + var showMessage = function(msg) { + that.element_message.className = 'netdata-message'; + that.element_message.innerHTML = msg; + this.element_message.style.fontSize = 'x-small'; + that.element_message.style.paddingTop = '0px'; + that.___messageHidden___ = undefined; + } - if(this.globalSelectionSyncAbility() === false) { - if(this.debug === true) - this.log('sync: cannot sync (yet?).'); + var showMessageIcon = function(icon) { + that.element_message.innerHTML = icon; + that.element_message.className = 'netdata-message icon'; + maxMessageFontSize(); + that.___messageHidden___ = undefined; + } - return; + var hideMessage = function() { + if(typeof that.___messageHidden___ === 'undefined') { + that.___messageHidden___ = true; + that.element_message.className = 'netdata-message hidden'; } } - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { - st.setSelection(t); - }); - } - - // stop syncing all charts to the given time - chartState.prototype.globalSelectionSyncStop = function() { - if(NETDATA.globalSelectionSync.slaves.length) { - if(this.debug === true) - this.log('sync: cleaning up...'); - - var self = this; - $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { - if(st === self) { - if(self.debug === true) - st.log('sync: not adding me to sync stop'); - } - else { - if(self.debug === true) - st.log('sync: removed slave from sync'); - - st.clearSelection(); - } - }); + var showRendering = function() { + var icon; + if(that.chart !== null) { + if(that.chart.chart_type === 'line') + icon = ''; + else + icon = ''; + } + else + icon = ''; - NETDATA.globalSelectionSync.slaves = []; - NETDATA.globalSelectionSync.state = null; + showMessageIcon(icon + ' netdata'); } - // since we are the sync master, we should not call this.clearSelection() - // dygraphs is taking care of visualizing our selection. - this.selected = false; - } - - chartState.prototype.setSelection = function(t) { - if(typeof this.library.setSelection === 'function') { - if(this.library.setSelection(this, t) === true) - this.selected = true; - else - this.selected = false; + var showLoading = function() { + if(that.chart_created === false) { + showMessageIcon(' netdata'); + return true; + } + return false; } - else this.selected = true; - if(this.selected === true && this.debug === true) - this.log('selection set to ' + t.toString()); + // hide the chart, when it is not visible - called from isVisible() + var hideChart = function() { + // no chart yet + if(that.chart_created === false) return; - return this.selected; - } + // we should destroy it + if(NETDATA.options.current.destroy_on_hide === true) { + init(); + that.___chartIsHidden___ = undefined; + return; + } - chartState.prototype.clearSelection = function() { - if(this.selected === true) { - if(typeof this.library.clearSelection === 'function') { - if(this.library.clearSelection(this) === true) - this.selected = false; - else - this.selected = true; + // just hide it, if it is not already hidden + if(typeof that.___chartIsHidden___ === 'undefined') { + showRendering(); + that.element_chart.style.display = 'none'; + if(that.element_legend !== null) that.element_legend.style.display = 'none'; + that.___chartIsHidden___ = true; + } + } + + // unhide the chart, when it is visible - called from isVisible() + var unhideChart = function() { + if(typeof that.___chartIsHidden___ !== 'undefined') { + that.element_chart.style.display = 'inline-block'; + if(that.element_legend !== null) that.element_legend.style.display = 'inline-block'; + hideMessage(); + that.___chartIsHidden___ = undefined; + resizeChart(); } - else this.selected = false; - - if(this.selected === false && this.debug === true) - this.log('selection cleared'); } - this.legendReset(); - return this.selected; - } - - // find if a timestamp (ms) is shown in the current chart - chartState.prototype.timeIsVisible = function(t) { - if(t >= this.data_after && t <= this.data_before) - return true; - return false; - }, + // ---------------------------------------------------------------------------------------------------------------- + // Chart Resize - chartState.prototype.calculateRowForTime = function(t) { - if(this.timeIsVisible(t) === false) return -1; - return Math.floor((t - this.data_after) / this.data_update_every); - } + // resizeChart() - private + // to be called just before the chart library to make sure that + // a properly sized dom is available + var resizeChart = function() { + if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) { + that.tm.last_resized = new Date().getTime(); + if(that.chart_created === false) return; - // ---------------------------------------------------------------------------------------------------------------- + if(that.needsRecreation()) + init(); - // console logging - chartState.prototype.log = function(msg) { - console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); - } + else if(typeof that.library.resize === 'function') { + that.library.resize(that); - chartState.prototype.pauseChart = function() { - if(this.paused === false) { - if(this.debug === true) - this.log('paused'); + if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null) + $(that.element_legend_childs.nano).nanoScroller(); - this.paused = true; + maxMessageFontSize(); + } + } } - } - - chartState.prototype.unpauseChart = function() { - if(this.paused) { - if(this.debug === true) - this.log('unpaused'); - this.paused = false; - } - } + // this is the actual chart resize algorithm + // it will: + // - resize the entire container + // - update the internal states + // - resize the chart as the div changes height + // - update the scrollbar of the legend + var resizeChartToHeight = function(h) { + // console.log(h); + that.element.style.height = h; - chartState.prototype.resetChart = function() { - if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible()) - NETDATA.globalPanAndZoom.clearMaster(); + if(that.settings_id !== null) + NETDATA.localStorageSet('chart_heights.' + that.settings_id, h); - this.tm.pan_and_zoom_seq = 0; + var now = new Date().getTime(); + NETDATA.options.last_page_scroll = now; + NETDATA.options.last_resized = now; + NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing; - this.clearSelection(); + // force a resize + that.tm.last_resized = 0; + resizeChart(); + }; - this.setMode('auto'); - this.current.force_update_at = 0; - this.current.force_before_ms = null; - this.current.force_after_ms = null; - this.tm.last_autorefreshed = 0; - this.paused = false; - this.selected = false; - this.enabled = true; - this.debug = false; - - // do not update the chart here - // or the chart will flip-flop when it is the master - // of a selection sync and another chart becomes - // the new master - if(NETDATA.options.current.sync_pan_and_zoom === false && this.isVisible() === true) - this.updateChart(); - } + this.resizeHandler = function(e) { + e.preventDefault(); - chartState.prototype.setMode = function(m) { - if(this.current) { - if(this.current.name === m) return; + if(typeof this.event_resize === 'undefined' + || this.event_resize.chart_original_w === 'undefined' + || this.event_resize.chart_original_h === 'undefined') + this.event_resize = { + chart_original_w: this.element.clientWidth, + chart_original_h: this.element.clientHeight, + last: 0 + }; - this[m].requested_before_ms = this.current.requested_before_ms; - this[m].requested_after_ms = this.current.requested_after_ms; - } + if(e.type === 'touchstart') { + this.event_resize.mouse_start_x = e.touches.item(0).pageX; + this.event_resize.mouse_start_y = e.touches.item(0).pageY; + } + else { + this.event_resize.mouse_start_x = e.clientX; + this.event_resize.mouse_start_y = e.clientY; + } - if(m === 'auto') - this.current = this.auto; - else if(m === 'pan') - this.current = this.pan; - else if(m === 'zoom') - this.current = this.zoom; - else - this.current = this.auto; + this.event_resize.chart_start_w = this.element.clientWidth; + this.event_resize.chart_start_h = this.element.clientHeight; + this.event_resize.chart_last_w = this.element.clientWidth; + this.event_resize.chart_last_h = this.element.clientHeight; - this.current.force_update_at = 0; - this.current.force_before_ms = null; - this.current.force_after_ms = null; + var now = new Date().getTime(); + if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) { + // double click / double tap event + + // the optimal height of the chart + // showing the entire legend + var optimal = this.event_resize.chart_last_h + + this.element_legend_childs.content.scrollHeight + - this.element_legend_childs.content.clientHeight; + + // if we are not optimal, be optimal + if(this.event_resize.chart_last_h != optimal) + resizeChartToHeight(optimal.toString() + 'px'); + + // else if we do not have the original height + // reset to the original height + else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h) + resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px'); + } + else { + this.event_resize.last = now; + + // process movement event + document.onmousemove = + document.ontouchmove = + this.element_legend_childs.resize_handler.onmousemove = + this.element_legend_childs.resize_handler.ontouchmove = + function(e) { + var y = null; + + switch(e.type) { + case 'mousemove': y = e.clientY; break; + case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break; + } - if(this.debug === true) - this.log('mode set to ' + this.current.name); - } + if(y !== null) { + var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y; - chartState.prototype.updateChartPanOrZoom = function(after, before) { - if(before < after) return false; + if(newH >= 70 && newH !== that.event_resize.chart_last_h) { + resizeChartToHeight(newH.toString() + 'px'); + that.event_resize.chart_last_h = newH; + } + } + }; + + // process end event + document.onmouseup = + document.ontouchend = + this.element_legend_childs.resize_handler.onmouseup = + this.element_legend_childs.resize_handler.ontouchend = + function(e) { + // remove all the hooks + document.onmouseup = + document.onmousemove = + document.ontouchmove = + document.ontouchend = + that.element_legend_childs.resize_handler.onmousemove = + that.element_legend_childs.resize_handler.ontouchmove = + that.element_legend_childs.resize_handler.onmouseout = + that.element_legend_childs.resize_handler.onmouseup = + that.element_legend_childs.resize_handler.ontouchend = + null; + + // allow auto-refreshes + NETDATA.options.auto_refresher_stop_until = 0; + }; + } + } - var min_duration = Math.round((this.chartWidth() / 30 * this.chart.update_every * 1000)); - if(this.debug === true) - this.log('requested duration of ' + ((before - after) / 1000).toString() + ' (' + after + ' - ' + before + '), minimum ' + min_duration / 1000); + var noDataToShow = function() { + this.legendUpdateDOM(); + that.tm.last_autorefreshed = new Date().getTime(); + that.data_update_every = 30 * 1000; + } - if((before - after) < min_duration) return false; + // ============================================================================================================ + // PUBLIC FUNCTIONS - var current_duration = this.data_before - this.data_after; - var wanted_duration = before - after; - var tolerance = this.data_update_every * 2; - var movement = Math.abs(before - this.data_before); + this.setMode = function(m) { + if(this.current !== null && this.current.name === m) return; - if(this.debug === true) - this.log('current duration: ' + current_duration / 1000 + ', wanted duration: ' + wanted_duration / 1000 + ', movement: ' + movement / 1000 + ', tolerance: ' + tolerance / 1000); + if(m === 'auto') + this.current = this.auto; + else if(m === 'pan') + this.current = this.pan; + else if(m === 'zoom') + this.current = this.zoom; + else + this.current = this.auto; - if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) { - if(this.debug === true) - this.log('IGNORED'); + this.current.force_update_at = 0; + this.current.force_before_ms = null; + this.current.force_after_ms = null; - return false; + this.tm.last_mode_switch = new Date().getTime(); } - if(this.current.name === 'auto') { - this.setMode('pan'); + // ---------------------------------------------------------------------------------------------------------------- + // global selection sync - if(this.debug === true) - this.log('updateChartPanOrZoom(): caller did not set proper mode'); + // prevent to global selection sync for some time + this.globalSelectionSyncDelay = function(ms) { + if(NETDATA.options.current.sync_selection === false) + return; + + if(typeof ms === 'number') + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms; + else + NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay; } - this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay; - this.current.force_after_ms = after; - this.current.force_before_ms = before; - NETDATA.globalPanAndZoom.setMaster(this, after, before); - return true; - } + // can we globally apply selection sync? + this.globalSelectionSyncAbility = function() { + if(NETDATA.options.current.sync_selection === false) + return false; - chartState.prototype.legendFormatValue = function(value) { - if(value === null || value === 'undefined') return '-'; - if(typeof value !== 'number') return value; + if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime()) + return false; - var abs = Math.abs(value); - if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString(); - if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString(); - return (Math.round(value * 10000) / 10000).toLocaleString(); - } + return true; + } + + this.globalSelectionSyncIsMaster = function() { + if(NETDATA.globalSelectionSync.state === this) + return true; + else + return false; + } - chartState.prototype.legendSetLabelValue = function(label, value) { - var series = this.element_legend_childs.series[label]; - if(typeof series === 'undefined') return; - if(series.value === null && series.user === null) return; + // this chart is the master of the global selection sync + this.globalSelectionSyncBeMaster = function() { + // am I the master? + if(this.globalSelectionSyncIsMaster()) { + if(this.debug === true) + this.log('sync: I am the master already.'); - // if the value has not changed, skip DOM update - //if(series.last === value) return; + return; + } - var s, r; - if(typeof value === 'number') { - var v = Math.abs(value); - s = r = this.legendFormatValue(value); + if(NETDATA.globalSelectionSync.state) { + if(this.debug === true) + this.log('sync: I am not the sync master. Resetting global sync.'); - if(typeof series.last === 'number') { - if(v > series.last) s += ''; - else if(v < series.last) s += ''; - else s += ''; + this.globalSelectionSyncStop(); } - else s += ''; - series.last = v; - } - else { - s = r = value; - series.last = value; - } - if(series.value !== null) series.value.innerHTML = s; - if(series.user !== null) series.user.innerHTML = r; - } + // become the master + if(this.debug === true) + this.log('sync: becoming sync master.'); - chartState.prototype.legendSetDate = function(ms) { - if(typeof ms !== 'number') { - this.legendShowUndefined(); - return; - } + this.selected = true; + NETDATA.globalSelectionSync.state = this; - var d = new Date(ms); + // find the all slaves + var targets = NETDATA.options.targets; + var len = targets.length; + while(len--) { + st = targets[len]; - if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString(); + if(st === this) { + if(this.debug === true) + st.log('sync: not adding me to sync'); + } + else if(st.globalSelectionSyncIsEligible()) { + if(this.debug === true) + st.log('sync: adding to sync as slave'); - if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString(); + st.globalSelectionSyncBeSlave(); + } + } - if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = this.chart.units; - } + // this.globalSelectionSyncDelay(100); + } - chartState.prototype.legendShowUndefined = function() { - if(this.element_legend_childs.title_date) - this.element_legend_childs.title_date.innerHTML = ' '; + // can the chart participate to the global selection sync as a slave? + this.globalSelectionSyncIsEligible = function() { + if(this.enabled === true + && this.library !== null + && typeof this.library.setSelection === 'function' + && this.isVisible() === true + && this.chart_created === true) + return true; - if(this.element_legend_childs.title_time) - this.element_legend_childs.title_time.innerHTML = this.chart.name; + return false; + } - if(this.element_legend_childs.title_units) - this.element_legend_childs.title_units.innerHTML = ' '; + // this chart becomes a slave of the global selection sync + this.globalSelectionSyncBeSlave = function() { + if(NETDATA.globalSelectionSync.state !== this) + NETDATA.globalSelectionSync.slaves.push(this); + } - if(this.data && this.element_legend_childs.series !== null) { - var labels = this.data.dimension_names; - var i = labels.length; - while(i--) { - var label = labels[i]; + // sync all the visible charts to the given time + // this is to be called from the chart libraries + this.globalSelectionSync = function(t) { + if(this.globalSelectionSyncAbility() === false) { + if(this.debug === true) + this.log('sync: cannot sync (yet?).'); - if(typeof label === 'undefined') continue; - if(typeof this.element_legend_childs.series[label] === 'undefined') continue; - this.legendSetLabelValue(label, null); + return; } - } - } - chartState.prototype.legendShowLatestValues = function() { - if(this.chart === null) return; - if(this.selected) return; - - if(this.data === null || this.element_legend_childs.series === null) { - this.legendShowUndefined(); - return; - } - - var show_undefined = true; - if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) - show_undefined = false; - - if(show_undefined) { - this.legendShowUndefined(); - return; - } + if(this.globalSelectionSyncIsMaster() === false) { + if(this.debug === true) + this.log('sync: trying to be sync master.'); - this.legendSetDate(this.data.before * 1000); + this.globalSelectionSyncBeMaster(); - var labels = this.data.dimension_names; - var i = labels.length; - while(i--) { - var label = labels[i]; + if(this.globalSelectionSyncAbility() === false) { + if(this.debug === true) + this.log('sync: cannot sync (yet?).'); - if(typeof label === 'undefined') continue; - if(typeof this.element_legend_childs.series[label] === 'undefined') continue; + return; + } + } - if(show_undefined) - this.legendSetLabelValue(label, null); - else - this.legendSetLabelValue(label, this.data.view_latest_values[i]); + NETDATA.globalSelectionSync.last_t = t; + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { + st.setSelection(t); + }); } - } - chartState.prototype.legendReset = function() { - this.legendShowLatestValues(); - } + // stop syncing all charts to the given time + this.globalSelectionSyncStop = function() { + if(NETDATA.globalSelectionSync.slaves.length) { + if(this.debug === true) + this.log('sync: cleaning up...'); - // this should be called just ONCE per dimension per chart - chartState.prototype._chartDimensionColor = function(label) { - if(this.colors === null) this.chartColors(); + $.each(NETDATA.globalSelectionSync.slaves, function(i, st) { + if(st === that) { + if(that.debug === true) + st.log('sync: not adding me to sync stop'); + } + else { + if(that.debug === true) + st.log('sync: removed slave from sync'); - if(typeof this.colors_assigned[label] === 'undefined') { - if(this.colors_available.length === 0) { - for(var i = 0, len = NETDATA.colors.length; i < len ; i++) - this.colors_available.push(NETDATA.colors[i]); - } + st.clearSelection(); + } + }); - this.colors_assigned[label] = this.colors_available.shift(); + NETDATA.globalSelectionSync.last_t = 0; + NETDATA.globalSelectionSync.slaves = []; + NETDATA.globalSelectionSync.state = null; + } - if(this.debug === true) - this.log('label "' + label + '" got color "' + this.colors_assigned[label]); - } - else { - if(this.debug === true) - this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"'); + // since we are the sync master, we should not call this.clearSelection() + // dygraphs is taking care of visualizing our selection. + this.selected = false; + this.clearSelection(); } - this.colors.push(this.colors_assigned[label]); - return this.colors_assigned[label]; - } + this.setSelection = function(t) { + if(typeof this.library.setSelection === 'function') { + if(this.library.setSelection(this, t) === true) + this.selected = true; + else + this.selected = false; + } + else this.selected = true; - chartState.prototype.chartColors = function() { - if(this.colors !== null) return this.colors; + if(this.selected === true && this.debug === true) + this.log('selection set to ' + t.toString()); - this.colors = new Array(); - this.colors_available = new Array(); - // this.colors_assigned = {}; + return this.selected; + } - var c = $(this.element).data('colors'); - if(typeof c !== 'undefined' && c !== null) { - if(typeof c !== 'string') { - this.log('invalid color given: ' + c + ' (give a space separated list of colors)'); - } - else { - c = c.split(' '); - for(var i = 0, len = c.length; i < len ; i++) - this.colors_available.push(c[i]); + this.clearSelection = function() { + if(this.selected === true) { + if(typeof this.library.clearSelection === 'function') { + if(this.library.clearSelection(this) === true) + this.selected = false; + else + this.selected = true; + } + else this.selected = false; + + if(this.selected === false && this.debug === true) + this.log('selection cleared'); } + + this.legendReset(); + return this.selected; } - // push all the standard colors too - for(var i = 0, len = NETDATA.colors.length; i < len ; i++) - this.colors_available.push(NETDATA.colors[i]); + // find if a timestamp (ms) is shown in the current chart + this.timeIsVisible = function(t) { + if(t >= this.data_after && t <= this.data_before) + return true; + return false; + }, - return this.colors; - } + this.calculateRowForTime = function(t) { + if(this.timeIsVisible(t) === false) return -1; + return Math.floor((t - this.data_after) / this.data_update_every); + } - chartState.prototype.legendUpdateDOM = function() { - var needed = false; + // ---------------------------------------------------------------------------------------------------------------- - // check that the legend DOM is up to date for the downloaded dimensions - if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) { - // this.log('the legend does not have any series - requesting legend update'); - needed = true; - } - else if(this.data === null) { - // this.log('the chart does not have any data - requesting legend update'); - needed = true; + // console logging + this.log = function(msg) { + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); } - else if(typeof this.element_legend_childs.series.labels_key === 'undefined') { - needed = true; - } - else { - var labels = this.data.dimension_names.toString(); - if(labels !== this.element_legend_childs.series.labels_key) { - needed = true; + this.pauseChart = function() { + if(this.paused === false) { if(this.debug === true) - this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"'); + this.log('paused'); + + this.paused = true; } } - if(needed === false) { - // make sure colors available - this.chartColors(); - - // do we have to update the current values? - // we do this, only when the visible chart is current - if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) { + this.unpauseChart = function() { + if(this.paused) { if(this.debug === true) - this.log('chart in running... updating values on legend...'); + this.log('unpaused'); - //var labels = this.data.dimension_names; - //var i = labels.length; - //while(i--) - // this.legendSetLabelValue(labels[i], this.data.latest_values[i]); + this.paused = false; } - return; } - if(this.colors === null) { - // this is the first time we update the chart - // let's assign colors to all dimensions - if(this.library.track_colors() === true) - for(var dim in this.chart.dimensions) - this._chartDimensionColor(this.chart.dimensions[dim].name); - } - // we will re-generate the colors for the chart - this.colors = null; - if(this.debug === true) - this.log('updating Legend DOM'); + this.resetChart = function() { + if(NETDATA.globalPanAndZoom.isMaster(this) && this.isVisible()) + NETDATA.globalPanAndZoom.clearMaster(); - // mark all dimensions as invalid - this.dimensions_visibility.invalidateAll(); - - var self = $(this.element); - var genLabel = function(state, parent, name, count) { - var color = state._chartDimensionColor(name); + this.tm.pan_and_zoom_seq = 0; - var user_element = null; - var user_id = self.data('show-value-of-' + name + '-at') || null; - if(user_id !== null) { - user_element = document.getElementById(user_id) || null; - if(user_element === null) - me.log('Cannot find element with id: ' + user_id); - } + this.clearSelection(); - state.element_legend_childs.series[name] = { - name: document.createElement('span'), - value: document.createElement('span'), - user: user_element, - last: null - }; + this.setMode('auto'); + this.current.force_update_at = 0; + this.current.force_before_ms = null; + this.current.force_after_ms = null; + this.tm.last_autorefreshed = 0; + this.paused = false; + this.selected = false; + this.enabled = true; + this.debug = false; - var label = state.element_legend_childs.series[name]; + // do not update the chart here + // or the chart will flip-flop when it is the master + // of a selection sync and another chart becomes + // the new master + if(NETDATA.options.current.sync_pan_and_zoom === false && this.isVisible() === true) + this.updateChart(); + } - // create the dimension visibility tracking for this label - var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color); + this.updateChartPanOrZoom = function(after, before) { + if(before < after) return false; - var rgb = NETDATA.colorHex2Rgb(color); - label.name.innerHTML = '
' + var min_duration = Math.round((this.chartWidth() / 30 * this.chart.update_every * 1000)); - var text = document.createTextNode(' ' + name); - label.name.appendChild(text); + if(this.debug === true) + this.log('requested duration of ' + ((before - after) / 1000).toString() + ' (' + after + ' - ' + before + '), minimum ' + min_duration / 1000); - if(count > 0) - parent.appendChild(document.createElement('br')); + if((before - after) < min_duration) return false; - parent.appendChild(label.name); - parent.appendChild(label.value); - }; + var current_duration = this.data_before - this.data_after; + var wanted_duration = before - after; + var tolerance = this.data_update_every * 2; + var movement = Math.abs(before - this.data_before); - var content = document.createElement('div'); - - if(this.hasLegend()) { - this.element_legend_childs = { - content: content, - resize_handler: document.createElement('div'), - title_date: document.createElement('span'), - title_time: document.createElement('span'), - title_units: document.createElement('span'), - nano: document.createElement('div'), - nano_options: { - paneClass: 'netdata-legend-series-pane', - sliderClass: 'netdata-legend-series-slider', - contentClass: 'netdata-legend-series-content', - enabledClass: '__enabled', - flashedClass: '__flashed', - activeClass: '__active', - tabIndex: -1, - alwaysVisible: true, - sliderMinHeight: 10 - }, - series: {} - }; + if(this.debug === true) + this.log('current duration: ' + current_duration / 1000 + ', wanted duration: ' + wanted_duration / 1000 + ', movement: ' + movement / 1000 + ', tolerance: ' + tolerance / 1000); - this.element_legend.innerHTML = ''; + if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance) { + if(this.debug === true) + this.log('IGNORED'); - this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler"; - this.element_legend_childs.resize_handler.innerHTML = ''; - this.element.appendChild(this.element_legend_childs.resize_handler); - var self2 = this; + return false; + } - // mousedown event - this.element_legend_childs.resize_handler.onmousedown = - function(e) { - self2.resizeHandler(e); - }; + if(this.current.name === 'auto') { + this.setMode('pan'); - // touchstart event - this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) { - self2.resizeHandler(e); - }, false); + if(this.debug === true) + this.log('updateChartPanOrZoom(): caller did not set proper mode'); + } - this.element_legend_childs.title_date.className += " netdata-legend-title-date"; - this.element_legend.appendChild(this.element_legend_childs.title_date); + this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay; + this.current.force_after_ms = after; + this.current.force_before_ms = before; + NETDATA.globalPanAndZoom.setMaster(this, after, before); + return true; + } - this.element_legend.appendChild(document.createElement('br')); + this.legendFormatValue = function(value) { + if(value === null || value === 'undefined') return '-'; + if(typeof value !== 'number') return value; - this.element_legend_childs.title_time.className += " netdata-legend-title-time"; - this.element_legend.appendChild(this.element_legend_childs.title_time); + var abs = Math.abs(value); + if(abs >= 1) return (Math.round(value * 100) / 100).toLocaleString(); + if(abs >= 0.1) return (Math.round(value * 1000) / 1000).toLocaleString(); + return (Math.round(value * 10000) / 10000).toLocaleString(); + } - this.element_legend.appendChild(document.createElement('br')); + this.legendSetLabelValue = function(label, value) { + var series = this.element_legend_childs.series[label]; + if(typeof series === 'undefined') return; + if(series.value === null && series.user === null) return; - this.element_legend_childs.title_units.className += " netdata-legend-title-units"; - this.element_legend.appendChild(this.element_legend_childs.title_units); + // if the value has not changed, skip DOM update + //if(series.last === value) return; - this.element_legend.appendChild(document.createElement('br')); + var s, r; + if(typeof value === 'number') { + var v = Math.abs(value); + s = r = this.legendFormatValue(value); - this.element_legend_childs.nano.className = 'netdata-legend-series'; - this.element_legend.appendChild(this.element_legend_childs.nano); + if(typeof series.last === 'number') { + if(v > series.last) s += ''; + else if(v < series.last) s += ''; + else s += ''; + } + else s += ''; + series.last = v; + } + else { + s = r = value; + series.last = value; + } - content.className = 'netdata-legend-series-content'; - this.element_legend_childs.nano.appendChild(content); - } - else { - this.element_legend_childs = { - content: content, - resize_handler: null, - title_date: null, - title_time: null, - title_units: null, - nano: null, - nano_options: null, - series: {} - }; + if(series.value !== null) series.value.innerHTML = s; + if(series.user !== null) series.user.innerHTML = r; } - if(this.data) { - this.element_legend_childs.series.labels_key = this.data.dimension_names.toString(); - if(this.debug === true) - this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"'); - - for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) { - genLabel(this, content, this.data.dimension_names[i], i); - } - } - else { - var tmp = new Array(); - for(var dim in this.chart.dimensions) { - tmp.push(this.chart.dimensions[dim].name); - genLabel(this, content, this.chart.dimensions[dim].name, i); + this.legendSetDate = function(ms) { + if(typeof ms !== 'number') { + this.legendShowUndefined(); + return; } - this.element_legend_childs.series.labels_key = tmp.toString(); - if(this.debug === true) - this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"'); - } - // create a hidden div to be used for hidding - // the original legend of the chart library - var el = document.createElement('div'); - this.element_legend.appendChild(el); - el.style.display = 'none'; + var d = new Date(ms); - this.element_legend_childs.hidden = document.createElement('div'); - el.appendChild(this.element_legend_childs.hidden); + if(this.element_legend_childs.title_date) + this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString(); - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options); + if(this.element_legend_childs.title_time) + this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString(); - this.legendShowLatestValues(); - } + if(this.element_legend_childs.title_units) + this.element_legend_childs.title_units.innerHTML = this.chart.units; + } - chartState.prototype.createChartDOM = function() { - if(this.debug === true) - this.log('creating DOM'); - - this.element_chart_id = this.library_name + '-' + this.uuid + '-chart'; - this.element_chart = document.createElement('div'); - this.element_chart.className += ' netdata-chart' + (this.hasLegend()?'-with-legend-right':'').toString(); - this.element_chart.className += ' netdata-' + this.library_name + '-chart' + (this.hasLegend()?'-with-legend-right':'').toString(); - this.element_chart.id = this.element_chart_id; - $(this.element_chart).data('netdata-state-object', this); - this.element.appendChild(this.element_chart); - - this.element_legend_id = this.library_name + '-' + this.uuid + '-legend'; - this.element_legend = document.createElement('div'); - this.element_legend.className += ' netdata-chart-legend'; - this.element_legend.className += ' netdata-' + this.library_name + '-legend'; - this.element_legend.id = this.element_legend_id; - $(this.element_legend).data('netdata-state-object', this); - - if(this.hasLegend() === false) - this.element_legend.style.display = 'none'; - else - this.element.appendChild(this.element_legend); + this.legendShowUndefined = function() { + if(this.element_legend_childs.title_date) + this.element_legend_childs.title_date.innerHTML = ' '; - this.element_legend_childs.series = null; - this.legendUpdateDOM(); + if(this.element_legend_childs.title_time) + this.element_legend_childs.title_time.innerHTML = this.chart.name; - // in case the user has an active global selection sync in place - // reset it - this.globalSelectionSyncStop(); - this.dom_created = true; - } + if(this.element_legend_childs.title_units) + this.element_legend_childs.title_units.innerHTML = ' '; - chartState.prototype.hasLegend = function() { - if(typeof this.___hasLegendCache___ !== 'undefined') - return this.___hasLegendCache___; + if(this.data && this.element_legend_childs.series !== null) { + var labels = this.data.dimension_names; + var i = labels.length; + while(i--) { + var label = labels[i]; - var leg = false; - if(this.library && this.library.legend(this) === 'right-side') { - var legend = $(this.element).data('legend') || 'yes'; - if(legend === 'yes') leg = true; + if(typeof label === 'undefined') continue; + if(typeof this.element_legend_childs.series[label] === 'undefined') continue; + this.legendSetLabelValue(label, null); + } + } } - this.___hasLegendCache___ = leg; - return leg; - } + this.legendShowLatestValues = function() { + if(this.chart === null) return; + if(this.selected) return; - chartState.prototype.legendWidth = function() { - return (this.hasLegend())?140:0; - } + if(this.data === null || this.element_legend_childs.series === null) { + this.legendShowUndefined(); + return; + } - chartState.prototype.legendHeight = function() { - return $(this.element).height(); - } + var show_undefined = true; + if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) + show_undefined = false; - chartState.prototype.chartWidth = function() { - return $(this.element).width() - this.legendWidth(); - } + if(show_undefined) { + this.legendShowUndefined(); + return; + } - chartState.prototype.chartHeight = function() { - return $(this.element).height(); - } + this.legendSetDate(this.data.before * 1000); - chartState.prototype.chartPixelsPerPoint = function() { - // force an options provided detail - var px = this.pixels_per_point; + var labels = this.data.dimension_names; + var i = labels.length; + while(i--) { + var label = labels[i]; - if(this.library && px < this.library.pixels_per_point(this)) - px = this.library.pixels_per_point(this); + if(typeof label === 'undefined') continue; + if(typeof this.element_legend_childs.series[label] === 'undefined') continue; - if(px < NETDATA.options.current.pixels_per_point) - px = NETDATA.options.current.pixels_per_point; + if(show_undefined) + this.legendSetLabelValue(label, null); + else + this.legendSetLabelValue(label, this.data.view_latest_values[i]); + } + } - return px; - } + this.legendReset = function() { + this.legendShowLatestValues(); + } - chartState.prototype.needsRecreation = function() { - return ( - this.dom_created === true - && this.chart_created === true - && this.library - && this.library.autoresize() === false - && this.tm.last_resized < NETDATA.options.last_resized - ); - } + // this should be called just ONCE per dimension per chart + this._chartDimensionColor = function(label) { + if(this.colors === null) this.chartColors(); - chartState.prototype.resizeChart = function() { - if(this.needsRecreation()) { - if(this.debug === true) - this.log('forcing re-generation due to window resize.'); + if(typeof this.colors_assigned[label] === 'undefined') { + if(this.colors_available.length === 0) { + for(var i = 0, len = NETDATA.colors.length; i < len ; i++) + this.colors_available.push(NETDATA.colors[i]); + } - this.destroyChart(); - } + this.colors_assigned[label] = this.colors_available.shift(); - this.tm.last_resized = new Date().getTime(); - } + if(this.debug === true) + this.log('label "' + label + '" got color "' + this.colors_assigned[label]); + } + else { + if(this.debug === true) + this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"'); + } - chartState.prototype.chartURL = function() { - var before; - var after; - if(NETDATA.globalPanAndZoom.isActive()) { - after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000); - before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000); - this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq; + this.colors.push(this.colors_assigned[label]); + return this.colors_assigned[label]; } - else { - before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before; - after = this.current.force_after_ms !== null ? Math.round(this.current.force_after_ms / 1000) : this.after; - this.tm.pan_and_zoom_seq = 0; + + this.chartColors = function() { + if(this.colors !== null) return this.colors; + + this.colors = new Array(); + this.colors_available = new Array(); + // this.colors_assigned = {}; + + var c = $(this.element).data('colors'); + if(typeof c !== 'undefined' && c !== null) { + if(typeof c !== 'string') { + this.log('invalid color given: ' + c + ' (give a space separated list of colors)'); + } + else { + c = c.split(' '); + for(var i = 0, len = c.length; i < len ; i++) + this.colors_available.push(c[i]); + } + } + + // push all the standard colors too + for(var i = 0, len = NETDATA.colors.length; i < len ; i++) + this.colors_available.push(NETDATA.colors[i]); + + return this.colors; } - this.current.requested_after_ms = after * 1000; - this.current.requested_before_ms = before * 1000; + this.legendUpdateDOM = function() { + var needed = false; - this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint()); + // check that the legend DOM is up to date for the downloaded dimensions + if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) { + // this.log('the legend does not have any series - requesting legend update'); + needed = true; + } + else if(this.data === null) { + // this.log('the chart does not have any data - requesting legend update'); + needed = true; + } + else if(typeof this.element_legend_childs.series.labels_key === 'undefined') { + needed = true; + } + else { + var labels = this.data.dimension_names.toString(); + if(labels !== this.element_legend_childs.series.labels_key) { + needed = true; - // build the data URL - this.data_url = this.chart.data_url; - this.data_url += "&format=" + this.library.format(); - this.data_url += "&points=" + this.data_points.toString(); - this.data_url += "&group=" + this.method; - this.data_url += "&options=" + this.library.options(); - this.data_url += '|jsonwrap'; + if(this.debug === true) + this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"'); + } + } - if(NETDATA.options.current.eliminate_zero_dimensions === true) - this.data_url += '|nonzero'; + if(needed === false) { + // make sure colors available + this.chartColors(); - if(after) - this.data_url += "&after=" + after.toString(); + // do we have to update the current values? + // we do this, only when the visible chart is current + if(Math.abs(this.data.last_entry - this.data.before) <= this.data.view_update_every) { + if(this.debug === true) + this.log('chart in running... updating values on legend...'); - if(before) - this.data_url += "&before=" + before.toString(); + //var labels = this.data.dimension_names; + //var i = labels.length; + //while(i--) + // this.legendSetLabelValue(labels[i], this.data.latest_values[i]); + } + return; + } + if(this.colors === null) { + // this is the first time we update the chart + // let's assign colors to all dimensions + if(this.library.track_colors() === true) + for(var dim in this.chart.dimensions) + this._chartDimensionColor(this.chart.dimensions[dim].name); + } + // we will re-generate the colors for the chart + this.colors = null; - if(this.dimensions) - this.data_url += "&dimensions=" + this.dimensions; + if(this.debug === true) + this.log('updating Legend DOM'); - if(NETDATA.options.debug.chart_data_url === true || this.debug === true) - this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name); - } + // mark all dimensions as invalid + this.dimensions_visibility.invalidateAll(); - chartState.prototype.redrawChart = function() { - if(this.data !== null) - this.updateChartWithData(this.data); - } + var genLabel = function(state, parent, name, count) { + var color = state._chartDimensionColor(name); - chartState.prototype.updateChartWithData = function(data) { - if(this.debug === true) - this.log('got data from netdata server'); + var user_element = null; + var user_id = self.data('show-value-of-' + name + '-at') || null; + if(user_id !== null) { + user_element = document.getElementById(user_id) || null; + if(user_element === null) + me.log('Cannot find element with id: ' + user_id); + } - this.data = data; - this.updates_counter++; + state.element_legend_childs.series[name] = { + name: document.createElement('span'), + value: document.createElement('span'), + user: user_element, + last: null + }; - var started = new Date().getTime(); - this.tm.last_updated = started; + var label = state.element_legend_childs.series[name]; - // if the result is JSON, find the latest update-every - if(typeof data === 'object') { - if(typeof data.view_update_every !== 'undefined') - this.data_update_every = data.view_update_every * 1000; + // create the dimension visibility tracking for this label + var ds = state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color); - if(typeof data.after !== 'undefined') - this.data_after = data.after * 1000; + var rgb = NETDATA.colorHex2Rgb(color); + label.name.innerHTML = '
' - if(typeof data.before !== 'undefined') - this.data_before = data.before * 1000; + var text = document.createTextNode(' ' + name); + label.name.appendChild(text); - if(typeof data.first_entry !== 'undefined') - this.netdata_first = data.first_entry * 1000; + if(count > 0) + parent.appendChild(document.createElement('br')); - if(typeof data.last_entry !== 'undefined') - this.netdata_last = data.last_entry * 1000; + parent.appendChild(label.name); + parent.appendChild(label.value); + }; - if(typeof data.points !== 'undefined') - this.data_points = data.points; + var content = document.createElement('div'); + + if(this.hasLegend()) { + this.element_legend_childs = { + content: content, + resize_handler: document.createElement('div'), + title_date: document.createElement('span'), + title_time: document.createElement('span'), + title_units: document.createElement('span'), + nano: document.createElement('div'), + nano_options: { + paneClass: 'netdata-legend-series-pane', + sliderClass: 'netdata-legend-series-slider', + contentClass: 'netdata-legend-series-content', + enabledClass: '__enabled', + flashedClass: '__flashed', + activeClass: '__active', + tabIndex: -1, + alwaysVisible: true, + sliderMinHeight: 10 + }, + series: {} + }; - data.state = this; - } + this.element_legend.innerHTML = ''; - if(this.debug === true) { - this.log('UPDATE No ' + this.updates_counter + ' COMPLETED'); + this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler"; + this.element_legend_childs.resize_handler.innerHTML = ''; + this.element.appendChild(this.element_legend_childs.resize_handler); - if(this.current.force_after_ms) - this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString()); - else - this.log('STATUS: forced: unset'); + // mousedown event + this.element_legend_childs.resize_handler.onmousedown = + function(e) { + that.resizeHandler(e); + }; - this.log('STATUS: requested: ' + (this.current.requested_after_ms / 1000).toString() + ' - ' + (this.current.requested_before_ms / 1000).toString()); - this.log('STATUS: rendered : ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString()); - this.log('STATUS: points : ' + (this.data_points).toString()); - } + // touchstart event + this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) { + that.resizeHandler(e); + }, false); - if(this.data_points === 0) { - this.noData(); - return; - } + this.element_legend_childs.title_date.className += " netdata-legend-title-date"; + this.element_legend.appendChild(this.element_legend_childs.title_date); - // this may force the chart to be re-created - this.resizeChart(); + this.element_legend.appendChild(document.createElement('br')); - if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) { - if(this.debug === true) - this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); + this.element_legend_childs.title_time.className += " netdata-legend-title-time"; + this.element_legend.appendChild(this.element_legend_childs.title_time); - this.chart_created = false; - } + this.element_legend.appendChild(document.createElement('br')); - if(this.chart_created === true - && this.dom_created === true - && typeof this.library.update === 'function') { + this.element_legend_childs.title_units.className += " netdata-legend-title-units"; + this.element_legend.appendChild(this.element_legend_childs.title_units); - if(this.debug === true) - this.log('updating chart...'); + this.element_legend.appendChild(document.createElement('br')); - // check and update the legend - this.legendUpdateDOM(); + this.element_legend_childs.nano.className = 'netdata-legend-series'; + this.element_legend.appendChild(this.element_legend_childs.nano); - this.updates_since_last_creation++; - if(NETDATA.options.debug.chart_errors === true) { - this.library.update(this, data); + content.className = 'netdata-legend-series-content'; + this.element_legend_childs.nano.appendChild(content); } else { - try { - this.library.update(this, data); - } - catch(err) { - this.error('chart failed to be updated as ' + this.library_name); - } + this.element_legend_childs = { + content: content, + resize_handler: null, + title_date: null, + title_time: null, + title_units: null, + nano: null, + nano_options: null, + series: {} + }; } - } - else { - if(this.debug === true) - this.log('creating chart...'); - this.createChartDOM(); - this.updates_since_last_creation = 0; + if(this.data) { + this.element_legend_childs.series.labels_key = this.data.dimension_names.toString(); + if(this.debug === true) + this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"'); - if(NETDATA.options.debug.chart_errors === true) { - this.library.create(this, data); - this.chart_created = true; + for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) { + genLabel(this, content, this.data.dimension_names[i], i); + } } else { - try { - this.library.create(this, data); - this.chart_created = true; - } - catch(err) { - this.error('chart failed to be created as ' + this.library_name); + var tmp = new Array(); + for(var dim in this.chart.dimensions) { + tmp.push(this.chart.dimensions[dim].name); + genLabel(this, content, this.chart.dimensions[dim].name, i); } + this.element_legend_childs.series.labels_key = tmp.toString(); + if(this.debug === true) + this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"'); } - } - // this.legendShowLatestValues(); - - // update the performance counters - var now = new Date().getTime(); - - // don't update last_autorefreshed if this chart is - // forced to be updated with global PanAndZoom - if(NETDATA.globalPanAndZoom.isActive()) - this.tm.last_autorefreshed = 0; - else { - if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes) - this.tm.last_autorefreshed = Math.round(now / this.data_update_every) * this.data_update_every; - else - this.tm.last_autorefreshed = now; - } - this.refresh_dt_ms = now - started; - NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms; + // create a hidden div to be used for hidding + // the original legend of the chart library + var el = document.createElement('div'); + if(this.element_legend !== null) + this.element_legend.appendChild(el); + el.style.display = 'none'; - if(this.refresh_dt_element) - this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString(); - } + this.element_legend_childs.hidden = document.createElement('div'); + el.appendChild(this.element_legend_childs.hidden); - chartState.prototype.updateChart = function(callback) { - // due to late initialization of charts and libraries - // we need to check this too - if(this.enabled === false) { - if(this.debug === true) - this.log('I am not enabled'); + if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) + $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options); - if(typeof callback === 'function') callback(); - return false; + this.legendShowLatestValues(); } - if(this.chart === null) { - var self = this; - this.getChart(function() { self.updateChart(callback); }); - return; - } + this.hasLegend = function() { + if(typeof this.___hasLegendCache___ !== 'undefined') + return this.___hasLegendCache___; - if(this.library.initialized === false) { - if(this.library.enabled === true) { - var self = this; - this.library.initialize(function() { self.updateChart(callback); }); - return; + var leg = false; + if(this.library && this.library.legend(this) === 'right-side') { + var legend = $(this.element).data('legend') || 'yes'; + if(legend === 'yes') leg = true; } - else { - this.error('chart library "' + this.library_name + '" is not available.'); - if(typeof callback === 'function') callback(); - return false; - } - } - - this.clearSelection(); - this.chartURL(); - this.showLoading(); - - if(this.debug === true) - this.log('updating from ' + this.data_url); - - var self = this; - this.xhr = $.ajax( { - url: this.data_url, - crossDomain: NETDATA.options.crossDomainAjax, - cache: false, - async: true - }) - .success(function(data) { - self.hideLoading(); - if(self.debug === true) - self.log('data received. updating chart.'); - - self.updateChartWithData(data); - }) - .fail(function() { - self.hideLoading(); - self.error('data download failed for url: ' + self.data_url); - }) - .always(function() { - self.hideLoading(); - if(typeof callback === 'function') callback(); - }); - } - - chartState.prototype.destroyChart = function() { - if(this.debug === true) - this.log('destroying chart'); - - if(this.element_message !== null) { - this.element_message.innerHTML = ''; - this.element_message = null; + this.___hasLegendCache___ = leg; + return leg; } - if(this.element_loading !== null) { - this.element_loading.innerHTML = ''; - this.element_loading = null; + this.legendWidth = function() { + return (this.hasLegend())?140:0; } - if(this.element_legend !== null) { - this.element_legend.innerHTML = ''; - this.element_legend = null; + this.legendHeight = function() { + return $(this.element).height(); } - if(this.element_chart !== null) { - this.element_chart.innerHTML = ''; - this.element_chart = null; + this.chartWidth = function() { + return $(this.element).width() - this.legendWidth(); } - this.element_legend_childs = { - hidden: null, - title_date: null, - title_time: null, - title_units: null, - nano: null, - nano_options: null, - series: null - }; - - this.element.innerHTML = ''; - this.refresh_dt_element = null; - - this.dom_created = false; - this.chart_created = false; - this.paused = false; - this.selected = false; - this.updates_counter = 0; - this.updates_since_last_creation = 0; - this.tm.pan_and_zoom_seq = 0; - this.tm.last_resized = 0; - this.tm.last_visible_check = 0; - this.tm.last_hidden = 0; - this.tm.last_unhidden = 0; - this.tm.last_autorefreshed = 0; - - this.data = null; - this.data_points = 0; - this.data_after = 0; - this.data_before = 0; - this.data_update_every = 0; - this.netdata_first = 0; - this.netdata_last = 0; - if(this.current !== null) { - this.current.force_update_at = 0; - this.current.force_after_ms = null; - this.current.force_before_ms = null; - this.current.requested_after_ms = null; - this.current.requested_before_ms = null; + this.chartHeight = function() { + return $(this.element).height(); } - this.init(); - } - chartState.prototype.unhideChart = function() { - if(typeof this.___isHidden___ !== 'undefined' && this.enabled === true) { - if(this.debug === true) - this.log('unhiding chart'); + this.chartPixelsPerPoint = function() { + // force an options provided detail + var px = this.pixels_per_point; - this.element_message.style.display = 'none'; - if(this.element_chart !== null) this.element_chart.style.display = 'inline-block'; - if(this.element_legend !== null) this.element_legend.style.display = 'inline-block'; - if(this.element_loading !== null) this.element_loading.style.display = 'none'; - this.___isHidden___ = undefined; - this.element_message.innerHTML = 'chart ' + this.id + ' is visible now'; + if(this.library && px < this.library.pixels_per_point(this)) + px = this.library.pixels_per_point(this); - // refresh the scrolbar - if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null) - $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options); + if(px < NETDATA.options.current.pixels_per_point) + px = NETDATA.options.current.pixels_per_point; - this.tm.last_unhidden = new Date().getTime(); + return px; } - } - chartState.prototype.hideChart = function() { - if(typeof this.___isHidden___ === 'undefined' && this.enabled === true) { - if(NETDATA.options.current.destroy_on_hide === true) - this.destroyChart(); - - if(this.debug === true) - this.log('hiding chart'); - - this.element_message.style.display = 'inline-block'; - if(this.element_chart !== null) this.element_chart.style.display = 'none'; - if(this.element_legend !== null) this.element_legend.style.display = 'none'; - if(this.element_loading !== null) this.element_loading.style.display = 'none'; - this.___isHidden___ = true; - this.___showsLoading___ = undefined; - this.element_message.innerHTML = 'chart ' + this.id + ' is hidden to speed up the browser'; - this.tm.last_hidden = new Date().getTime(); + this.needsRecreation = function() { + return ( + this.chart_created === true + && this.library + && this.library.autoresize() === false + && this.tm.last_resized < NETDATA.options.last_resized + ); } - } - chartState.prototype.hideLoading = function() { - if(typeof this.___showsLoading___ !== 'undefined' && this.enabled === true) { - if(this.debug === true) - this.log('hide loading...'); - - this.element_message.style.display = 'none'; - if(this.element_chart !== null) this.element_chart.style.display = 'inline-block'; - if(this.element_legend !== null) this.element_legend.style.display = 'inline-block'; - if(this.element_loading !== null) this.element_loading.style.display = 'none'; - this.___showsLoading___ = undefined; - this.element_loading.innerHTML = 'chart ' + this.id + ' finished loading!'; - this.tm.last_unhidden = new Date().getTime(); - } - } + this.chartURL = function() { + var before; + var after; + if(NETDATA.globalPanAndZoom.isActive()) { + after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000); + before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000); + this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq; + } + else { + before = this.current.force_before_ms !== null ? Math.round(this.current.force_before_ms / 1000) : this.before; + after = this.current.force_after_ms !== null ? Math.round(this.current.force_after_ms / 1000) : this.after; + this.tm.pan_and_zoom_seq = 0; + } - chartState.prototype.showLoading = function() { - if(typeof this.___showsLoading___ === 'undefined' && this.chart_created === false && this.enabled === true) { - if(this.debug === true) - this.log('show loading...'); + this.requested_after = after * 1000; + this.requested_before = before * 1000; - this.element_message.style.display = 'none'; - if(this.element_chart !== null) this.element_chart.style.display = 'none'; - if(this.element_legend !== null) this.element_legend.style.display = 'none'; - if(this.element_loading !== null) this.element_loading.style.display = 'inline-block'; - this.___showsLoading___ = true; - this.___isHidden___ = undefined; - this.element_loading.innerHTML = 'chart ' + this.id + ' is loading...'; - this.tm.last_hidden = new Date().getTime(); - } - } + this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint()); - chartState.prototype.isVisible = function() { - // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); + // build the data URL + this.data_url = this.chart.data_url; + this.data_url += "&format=" + this.library.format(); + this.data_url += "&points=" + this.data_points.toString(); + this.data_url += "&group=" + this.method; + this.data_url += "&options=" + this.library.options(); + this.data_url += '|jsonwrap'; - // caching - we do not evaluate the charts visibility - // if the page has not been scrolled since the last check - if(this.tm.last_visible_check > NETDATA.options.last_page_scroll) { - if(this.debug === true) - this.log('isVisible: ' + this.___isVisible___); + if(NETDATA.options.current.eliminate_zero_dimensions === true) + this.data_url += '|nonzero'; - return this.___isVisible___; - } + if(after) + this.data_url += "&after=" + after.toString(); - this.tm.last_visible_check = new Date().getTime(); + if(before) + this.data_url += "&before=" + before.toString(); - var wh = window.innerHeight; - var x = this.element.getBoundingClientRect(); - var ret = 0; - var tolerance = 0; + if(this.dimensions) + this.data_url += "&dimensions=" + this.dimensions; - if(x.top < 0 && -x.top > x.height) { - // the chart is entirely above - ret = -x.top - x.height; + if(NETDATA.options.debug.chart_data_url === true || this.debug === true) + this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name); } - else if(x.top > wh) { - // the chart is entirely below - ret = x.top - wh; + + this.redrawChart = function() { + if(this.data !== null) + this.updateChartWithData(this.data); } - if(ret > tolerance) { - // the chart is too far - this.___isVisible___ = false; - if(this.chart_created === true) this.hideChart(); - + this.updateChartWithData = function(data) { if(this.debug === true) - this.log('isVisible: ' + this.___isVisible___); + this.log('got data from netdata server'); - return this.___isVisible___; - } - else { - // the chart is inside or very close - this.___isVisible___ = true; - this.unhideChart(); - - if(this.debug === true) - this.log('isVisible: ' + this.___isVisible___); + // this may force the chart to be re-created + resizeChart(); - return this.___isVisible___; - } - } + this.data = data; + this.updates_counter++; - chartState.prototype.isAutoRefreshed = function() { - return (this.current.autorefresh); - } + var started = new Date().getTime(); - chartState.prototype.canBeAutoRefreshed = function() { - now = new Date().getTime(); + // if the result is JSON, find the latest update-every + if(typeof data === 'object') { + if(typeof data.view_update_every !== 'undefined') + this.data_update_every = data.view_update_every * 1000; - if(this.enabled === false) { - if(this.debug === true) - this.log('I am not enabled'); + if(typeof data.after !== 'undefined') + this.data_after = data.after * 1000; - return false; - } + if(typeof data.before !== 'undefined') + this.data_before = data.before * 1000; - if(this.library === null || this.library.enabled === false) { - this.error('charting library "' + this.library_name + '" is not available'); - if(this.debug === true) - this.log('My chart library ' + this.library_name + ' is not available'); + if(typeof data.first_entry !== 'undefined') + this.netdata_first = data.first_entry * 1000; - return false; - } + if(typeof data.last_entry !== 'undefined') + this.netdata_last = data.last_entry * 1000; - if(this.isVisible() === false) { - if(NETDATA.options.debug.visibility === true || this.debug === true) - this.log('I am not visible'); + if(typeof data.points !== 'undefined') + this.data_points = data.points; - return false; - } - - if(this.current.force_update_at !== 0 && this.current.force_update_at < now) { - if(this.debug === true) - this.log('timed force update detected - allowing this update'); + data.state = this; + } - this.current.force_update_at = 0; - return true; - } + if(this.debug === true) { + this.log('UPDATE No ' + this.updates_counter + ' COMPLETED'); + + if(this.current.force_after_ms) + this.log('STATUS: forced : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString()); + else + this.log('STATUS: forced: unset'); - if(this.isAutoRefreshed() === true) { - // allow the first update, even if the page is not visible - if(this.updates_counter && NETDATA.options.page_is_visible === false) { - if(NETDATA.options.debug.focus === true || this.debug === true) - this.log('canBeAutoRefreshed(): page does not have focus'); + this.log('STATUS: requested: ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString()); + this.log('STATUS: rendered : ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString()); + this.log('STATUS: points : ' + (this.data_points).toString()); + } - return false; + if(this.data_points === 0) { + noDataToShow(); + return; } - if(this.needsRecreation() === true) { + if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) { if(this.debug === true) - this.log('canBeAutoRefreshed(): needs re-creation.'); + this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); - return true; + this.chart_created = false; } - // options valid only for autoRefresh() - if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) { - if(NETDATA.globalPanAndZoom.isActive()) { - if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): global panning: I need an update.'); + // check and update the legend + this.legendUpdateDOM(); - return true; - } - else { - if(this.debug === true) - this.log('canBeAutoRefreshed(): global panning: I am already up to date.'); + if(this.chart_created === true + && typeof this.library.update === 'function') { - return false; + if(this.debug === true) + this.log('updating chart...'); + + this.updates_since_last_creation++; + if(NETDATA.options.debug.chart_errors === true) { + this.library.update(this, data); + } + else { + try { + this.library.update(this, data); + } + catch(err) { + error('chart failed to be updated as ' + this.library_name); } } + } + else { + if(this.debug === true) + this.log('creating chart...'); - if(this.selected === true) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): I have a selection in place.'); - - return false; + if(NETDATA.options.debug.chart_errors === true) { + this.library.create(this, data); + this.chart_created = true; + this.updates_since_last_creation = 0; } - - if(this.paused === true) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): I am paused.'); - - return false; + else { + try { + this.library.create(this, data); + this.chart_created = true; + this.updates_since_last_creation = 0; + } + catch(err) { + error('chart failed to be created as ' + this.library_name); + } } + } + hideMessage(); + NETDATA.globalSelectionSync.stop(); - if(now - this.tm.last_autorefreshed >= this.data_update_every) { - if(this.debug === true) - this.log('canBeAutoRefreshed(): It is time to update me.'); + // this.legendShowLatestValues(); - return true; - } + // update the performance counters + var now = new Date().getTime(); + this.tm.last_updated = now; + + // don't update last_autorefreshed if this chart is + // forced to be updated with global PanAndZoom + if(NETDATA.globalPanAndZoom.isActive()) + this.tm.last_autorefreshed = 0; + else { + if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes) + this.tm.last_autorefreshed = Math.round(now / this.data_update_every) * this.data_update_every; + else + this.tm.last_autorefreshed = now; } - } - return false; - } + this.refresh_dt_ms = now - started; + NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms; - chartState.prototype.autoRefresh = function(callback) { - if(this.canBeAutoRefreshed() === true) { - this.updateChart(callback); - } - else { - if(typeof callback !== 'undefined') - callback(); + if(this.refresh_dt_element !== null) + this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString(); } - } - chartState.prototype._defaultsFromDownloadedChart = function(chart) { - this.chart = chart; - this.chart_url = chart.url; - this.data_update_every = chart.update_every * 1000; - this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint()); - this.tm.last_info_downloaded = new Date().getTime(); - } + this.updateChart = function(callback) { + // due to late initialization of charts and libraries + // we need to check this too + if(this.enabled === false) { + if(this.debug === true) + this.log('I am not enabled'); - // fetch the chart description from the netdata server - chartState.prototype.getChart = function(callback) { - this.chart = NETDATA.chartRegistry.get(this.host, this.id); - if(this.chart) { - this._defaultsFromDownloadedChart(this.chart); - if(typeof callback === 'function') callback(); - } - else { - this.chart_url = this.host + "/api/v1/chart?chart=" + this.id; + if(typeof callback === 'function') callback(); + return false; + } - if(this.debug === true) - this.log('downloading ' + this.chart_url); + if(this.chart === null) { + this.getChart(function() { that.updateChart(callback); }); + return; + } - var self = this; + if(this.library.initialized === false) { + if(this.library.enabled === true) { + this.library.initialize(function() { that.updateChart(callback); }); + return; + } + else { + error('chart library "' + this.library_name + '" is not available.'); + if(typeof callback === 'function') callback(); + return false; + } + } + + this.clearSelection(); + this.chartURL(); - $.ajax( { - url: this.chart_url, + if(this.debug === true) + this.log('updating from ' + this.data_url); + + this.xhr = $.ajax( { + url: this.data_url, crossDomain: NETDATA.options.crossDomainAjax, cache: false, async: true }) - .done(function(chart) { - chart.url = self.chart_url; - chart.data_url = (self.host + chart.data_url); - self._defaultsFromDownloadedChart(chart); - NETDATA.chartRegistry.add(self.host, self.id, chart); + .success(function(data) { + if(that.debug === true) + that.log('data received. updating chart.'); + + that.updateChartWithData(data); }) .fail(function() { - NETDATA.error(404, self.chart_url); - self.error('chart not found on url "' + self.chart_url + '"'); + error('data download failed for url: ' + that.data_url); }) .always(function() { if(typeof callback === 'function') callback(); }); } - } - // resize the chart to its real dimensions - // as given by the caller - chartState.prototype.sizeChart = function() { - if(this.hasLegend() === true) - this.element.className += " netdata-container-with-legend"; - else - this.element.className += " netdata-container"; + this.isVisible = function() { + // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); - if(this.debug === true) - this.log('sizing element'); + // caching - we do not evaluate the charts visibility + // if the page has not been scrolled since the last check + if(this.tm.last_visible_check > NETDATA.options.last_page_scroll) { + if(this.debug === true) + this.log('isVisible: ' + this.___isVisible___); - if(this.width !== 0) - $(this.element).css('width', this.width); + return this.___isVisible___; + } - if(this.height !== 0) - $(this.element).css('height', this.height); + this.tm.last_visible_check = new Date().getTime(); - if(NETDATA.chartDefaults.min_width !== null) - $(this.element).css('min-width', NETDATA.chartDefaults.min_width); - } + var wh = window.innerHeight; + var x = this.element.getBoundingClientRect(); + var ret = 0; + var tolerance = 0; - chartState.prototype.noData = function() { - if(this.dom_created === false) - this.createChartDOM(); + if(x.top < 0 && -x.top > x.height) { + // the chart is entirely above + ret = -x.top - x.height; + } + else if(x.top > wh) { + // the chart is entirely below + ret = x.top - wh; + } - this.tm.last_autorefreshed = new Date().getTime(); - this.data_update_every = 30 * 1000; - } + if(ret > tolerance) { + // the chart is too far - // show a message in the chart - chartState.prototype.message = function(type, msg) { - this.hideChart(); - this.element_message.innerHTML = msg; + hideChart(); + this.___isVisible___ = false; + + if(this.debug === true) + this.log('isVisible: ' + this.___isVisible___); - if(this.debug === null) - this.log(msg); - } + return this.___isVisible___; + } + else { + // the chart is inside or very close - // show an error on the chart and stop it forever - chartState.prototype.error = function(msg) { - this.message('error', this.id + ': ' + msg); - this.enabled = false; - } + unhideChart(); + this.___isVisible___ = true; + + if(this.debug === true) + this.log('isVisible: ' + this.___isVisible___); - // show a message indicating the chart is loading - chartState.prototype.info = function(msg) { - this.message('info', this.id + ': ' + msg); - } + return this.___isVisible___; + } + } - chartState.prototype.init = function() { - this.element.innerHTML = ''; + this.isAutoRefreshed = function() { + return (this.current.autorefresh); + } - this.element_message = document.createElement('div'); - this.element_message.className += ' netdata-message'; - this.element.appendChild(this.element_message); + this.canBeAutoRefreshed = function() { + now = new Date().getTime(); - this.element_loading = document.createElement('div'); - this.element_loading.className += ' netdata-chart-is-loading'; - this.element.appendChild(this.element_loading); + if(this.enabled === false) { + if(this.debug === true) + this.log('I am not enabled'); - if(this.debug === null) - this.log('created'); + return false; + } - // make sure the host does not end with / - // all netdata API requests use absolute paths - while(this.host.slice(-1) === '/') - this.host = this.host.substring(0, this.host.length - 1); + if(this.library === null || this.library.enabled === false) { + error('charting library "' + this.library_name + '" is not available'); + if(this.debug === true) + this.log('My chart library ' + this.library_name + ' is not available'); - // check the requested library is available - // we don't initialize it here - it will be initialized when - // this chart will be first used - if(typeof NETDATA.chartLibraries[this.library_name] === 'undefined') { - NETDATA.error(402, this.library_name); - this.error('chart library "' + this.library_name + '" is not found'); + return false; + } + + if(this.isVisible() === false) { + if(NETDATA.options.debug.visibility === true || this.debug === true) + this.log('I am not visible'); + + return false; + } + + if(this.current.force_update_at !== 0 && this.current.force_update_at < now) { + if(this.debug === true) + this.log('timed force update detected - allowing this update'); + + this.current.force_update_at = 0; + return true; + } + + if(this.isAutoRefreshed() === true) { + // allow the first update, even if the page is not visible + if(this.updates_counter && NETDATA.options.page_is_visible === false) { + if(NETDATA.options.debug.focus === true || this.debug === true) + this.log('canBeAutoRefreshed(): page does not have focus'); + + return false; + } + + if(this.needsRecreation() === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): needs re-creation.'); + + return true; + } + + // options valid only for autoRefresh() + if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) { + if(NETDATA.globalPanAndZoom.isActive()) { + if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): global panning: I need an update.'); + + return true; + } + else { + if(this.debug === true) + this.log('canBeAutoRefreshed(): global panning: I am already up to date.'); + + return false; + } + } + + if(this.selected === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): I have a selection in place.'); + + return false; + } + + if(this.paused === true) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): I am paused.'); + + return false; + } + + if(now - this.tm.last_autorefreshed >= this.data_update_every) { + if(this.debug === true) + this.log('canBeAutoRefreshed(): It is time to update me.'); + + return true; + } + } + } + + return false; } - else if(NETDATA.chartLibraries[this.library_name].enabled === false) { - NETDATA.error(403, this.library_name); - this.error('chart library "' + this.library_name + '" is not enabled'); + + this.autoRefresh = function(callback) { + if(this.canBeAutoRefreshed() === true) { + this.updateChart(callback); + } + else { + if(typeof callback !== 'undefined') + callback(); + } } - else - this.library = NETDATA.chartLibraries[this.library_name]; - this.sizeChart(); + this._defaultsFromDownloadedChart = function(chart) { + this.chart = chart; + this.chart_url = chart.url; + this.data_update_every = chart.update_every * 1000; + this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint()); + this.tm.last_info_downloaded = new Date().getTime(); + } - // if we need to report the rendering speed - // find the element that needs to be updated - if(this.refresh_dt_element_name) - this.refresh_dt_element = document.getElementById(this.refresh_dt_element_name) || null; + // fetch the chart description from the netdata server + this.getChart = function(callback) { + this.chart = NETDATA.chartRegistry.get(this.host, this.id); + if(this.chart) { + this._defaultsFromDownloadedChart(this.chart); + if(typeof callback === 'function') callback(); + } + else { + this.chart_url = this.host + "/api/v1/chart?chart=" + this.id; + + if(this.debug === true) + this.log('downloading ' + this.chart_url); + + $.ajax( { + url: this.chart_url, + crossDomain: NETDATA.options.crossDomainAjax, + cache: false, + async: true + }) + .done(function(chart) { + chart.url = that.chart_url; + chart.data_url = (that.host + chart.data_url); + that._defaultsFromDownloadedChart(chart); + NETDATA.chartRegistry.add(that.host, that.id, chart); + }) + .fail(function() { + NETDATA.error(404, that.chart_url); + error('chart not found on url "' + that.chart_url + '"'); + }) + .always(function() { + if(typeof callback === 'function') callback(); + }); + } + } + + // ============================================================================================================ + // INITIALIZATION - // the default mode for all charts - this.setMode('auto'); + init(); } // get or create a chart state, given a DOM element @@ -3283,9 +3254,10 @@ y: { pixelsPerLabel: 15, valueFormatter: function (x) { + // we format legends with the state object + // no need to do anything here // return (Math.round(x*100) / 100).toLocaleString(); - //return state.legendFormatValue(x); - //FIXME + // return state.legendFormatValue(x); return x; } } @@ -3702,7 +3674,7 @@ NETDATA.morrisChartCreate = function(state, data) { state.morris_options = { - element: state.element_chart_id, + element: state.element_chart.id, data: data.result.data, xkey: 'time', ykeys: data.dimension_names, diff --git a/web/index.html b/web/index.html index 40d12594..a799a760 100755 --- a/web/index.html +++ b/web/index.html @@ -253,7 +253,7 @@ - +