]> arthur.barton.de Git - netdata.git/commitdiff
fixed minor issues throughout the code (mainly types); dashboard has now a watermark...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Tue, 29 Dec 2015 02:21:19 +0000 (04:21 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Tue, 29 Dec 2015 02:21:19 +0000 (04:21 +0200)
32 files changed:
src/appconfig.c
src/apps_plugin.c
src/avl.c
src/avl.h
src/common.c
src/common.h
src/daemon.c
src/dictionary.c
src/global_statistics.c
src/log.c
src/main.c
src/plugin_idlejitter.c
src/plugin_proc.c
src/plugin_proc.h
src/plugin_tc.c
src/plugins_d.c
src/popen.c
src/proc_diskstats.c
src/procfile.c
src/rrd.c
src/rrd.h
src/rrd2json.c
src/rrd2json.h
src/storage_number.c
src/web_buffer.c
src/web_buffer.h
src/web_client.c
src/web_client.h
src/web_server.c
web/dashboard.css
web/dashboard.js
web/index.html

index 3ba7b91928e43b3dd68e81aa891632274eab4210..7cbb053b455dd8e33e491e492e597086c4651c98 100755 (executable)
@@ -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';
index a7285c4f25c27a44acce57f165e4851a9f9845a7..83d5488cb578acf52bf35803e9ed5e9381f1817b 100755 (executable)
@@ -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;    /* <asm/param.h> */
+       myhz = HZ;    /* <asm/param.h> */
 #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);
index f5d67dc20f1c9a08e4b764ac522eab20f5a74d3f..4eb0ce0e4b272b3df6e7f8e7ca8587687805a98b 100755 (executable)
--- 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
index 888eed19298a4f489c79477bc1730ec6310285f3..cbcc412117eb3a8640a9149c71f64d707212d224 100755 (executable)
--- a/src/avl.h
+++ b/src/avl.h
  * Released under GNU General Public License (GPL) version 2
  *
  */
-#include <pthread.h>
-
 #ifndef _AVL_H
 #define _AVL_H 1
 
+#include <pthread.h>
+
 // #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
  */
index bf713195e1b1602a34e902d0cbc70b96b4561479..bde93500b4ed0e654984f49c82cd777edf462ef0 100755 (executable)
@@ -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
index ba00af74fda609fcda6efc9e944d4caae6d2bd56..9d8836f8bf9427ad9029c18b08ed1030afa1290b 100755 (executable)
@@ -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);
 
index 4cf89ec397f244bfc7cb435b27afad21e31e8cd8..268814798af9543714b30192ce23f62ff3dff0d5 100755 (executable)
@@ -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);
                }
index 033717f51dadc2d32b5dd3d94ba93ba709ae24f7..31f4d52e1990e057f5774b845332e7fe848eaff3 100755 (executable)
@@ -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 {
index 1b8c5d6f53c2583f55f90446d594c2ac2ff84d28..d4a04efd2d067aa32c7bf6d085a998e3522c449c 100755 (executable)
@@ -3,7 +3,6 @@
 #endif
 #include <pthread.h>
 
-#include "common.h"
 #include "global_statistics.h"
 
 struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL };
index ef452d41a3b6f18976c86893295d83f9eadac475..7ab3f1a51b706597e9943a68729101ab69e098d7 100755 (executable)
--- 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];
index 953a7e665c4e0b1379b7efaf177f23c121e4f2fd..3f0c5713ebdf8ce724679ab20075c82a9540abf9 100755 (executable)
@@ -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;
index b5ec8009647a07b5eafd8797b7895b08c277cd9c..8f55a002ce96fd111a119ab42c07f5123d1c5fbb 100755 (executable)
@@ -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;
index 9d7a48d6ee10919bd3f8d042adff921ad1542b2c..656ac1738fb928feb9b2cde2f77436bad540061c 100755 (executable)
@@ -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));
index 41ba785c38735c95905e054adf537613fb11c969..dcbd8dbb60122d2671628ed3ea414c7c9858e190 100755 (executable)
@@ -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 */
index 1a4be02d4452fc729ac8a6acf6e60286566fdfef..ae6f4ad11079063ec67294a54bd99a6ee5ac427c 100755 (executable)
@@ -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;
index b99f668d6d865a204af0075c9b626465129bf5f0..6aae68e11c39fc3f76ba56b64427c489d2540f6e 100755 (executable)
@@ -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;
index 9449c64f42d6ced1c7950be8d25d94bd7e4c18b7..4f721f5ebab59117460530dfbe971ab75ce808c1 100755 (executable)
@@ -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);
index 8e4da0eb3f7d581a337d3ed942c5a0e71be927be..15e38e4387acfc78bc007776bd0b52171d9b9557 100755 (executable)
 #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;
index 5bd1b10e634298a1d057fee2027a5236ed651d79..7a48579593caceb6fdfccbab710f359acc5d1142 100755 (executable)
@@ -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);
index 3881d51320684b32e714ef1b1e86d89dd51e0ab4..6a08a149048103c24febdbcb7ad8532c44938a26 100755 (executable)
--- 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;
index ab9935ad3dda4a79c4ae23f8bab6b1d1753f6aa2..b482d3e4e08c2b3aae21226bdf6be47ecd6ea6fc 100755 (executable)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -1,5 +1,6 @@
 #include <sys/time.h>
 #include <pthread.h>
+#include <stdint.h>
 
 #include "avl.h"
 #include "storage_number.h"
index 1e718b48eeb7cea1f0fc9699565d7a95c013b353..ce5d26e35e044990e0a2bfe95c3df701e465a0be 100755 (executable)
@@ -2,9 +2,9 @@
 #include <config.h>
 #endif
 #include <pthread.h>
-#include <sys/time.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdint.h>
 
 #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);
index 353145ab312092d80ed3938cf6f7bca288cb09f9..87ba205a80b26ba196c52a6ac6bb2e3f5c90634d 100755 (executable)
@@ -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);
 
index 2936c24b56750406ebd57fcdc3513bad2d6777ed..225cf034888418133d31b468823e84bbf5238063 100755 (executable)
@@ -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 );
 }
index c2e6b03c8e05f9ed273ac24a5b34f0b2e1b725dc..7a169eb40a51d25c54c8bde253c166c31329bf89 100755 (executable)
@@ -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);
index c282727ab58125bf575249f20401b8e69e7f55cf..4f01f1990741c93d87401922b3e68e84cd9e7354 100755 (executable)
@@ -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);
index f294e4bc643ed3cd1ad7c8a5f17e6c281052b4d6..5f5ff78afa2da6ef5025cce79a4d4b586caf7fd9 100755 (executable)
@@ -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';
 
index a32fce318a90f40103ac92c4d888a1af2f67d9cd..9762dcc3df36dfe25bbd6ed05c8a971e1a90c4a6 100755 (executable)
@@ -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
index fbfc034ab3eaa1f1e8a8ad1ac016c90309fc97b9..cae94acadd4c9edf689bb3aa9a97b09ede3bb222 100755 (executable)
@@ -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.");
index 36b08960e173934e687ef300a463a4ba31570d10..6cb3a58298918f3ddf9f8b52af838abe4df91f6c 100755 (executable)
@@ -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) */
index 5b5e0b4b8c5eab1905076af00c8a9ef0b71da6ba..ea084348af246af09ff16152e99466b81b97773f 100755 (executable)
                        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;
 
                // 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();
        }
 
        // ----------------------------------------------------------------------------------------------------------------
        // dimensions selection
 
        // FIXME
+       // move color assignment to dimensions, here
 
        dimensionStatus = function(parent, label, name_div, value_div, color) {
                this.enabled = false;
 
 
        // ----------------------------------------------------------------------------------------------------------------
-       // 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();
                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 = '<i class="fa fa-line-chart"></i>';
+                               else
+                                       icon = '<i class="fa fa-area-chart"></i>';
+                       }
+                       else
+                               icon = '<i class="fa fa-area-chart"></i>';
 
-                       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('<i class="fa fa-refresh"></i> 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 += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                               else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                               else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                               this.globalSelectionSyncStop();
                        }
-                       else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                       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 = '&nbsp;';
+               // 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 = '&nbsp;';
+               // 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 = '<table class="netdata-legend-name-table-'
-                               + state.chart.chart_type
-                               + '" style="background-color: '
-                               + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
-                               + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
+                       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 = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
-                       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 += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                                       else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                                       else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                               }
+                               else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                               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 = '&nbsp;';
 
-               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 = '&nbsp;';
 
-       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 = '<table class="netdata-legend-name-table-'
+                                       + state.chart.chart_type
+                                       + '" style="background-color: '
+                                       + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
+                                       + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
 
-                       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 = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
+                               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
                                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;
                                        }
                                }
        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,
index 40d1259491e7ed97add08e90c441c2d758a31a82..a799a76039d4851e5d8216bbe0420d09c44c5941 100755 (executable)
        <!-- <script> netdataServer = "http://box:19999"; </script> -->
 
        <!-- load the dashboard manager - it will do the rest -->
-       <script type="text/javascript" src="dashboard.js?v8"></script>
+       <script type="text/javascript" src="dashboard.js?v9"></script>
 </head>
 
 <body data-spy="scroll" data-target="#sidebar">