]> arthur.barton.de Git - netdata.git/commitdiff
added jsonwrap api option to allow javascript get information about the result set...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 2 Dec 2015 22:19:29 +0000 (00:19 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 2 Dec 2015 22:19:29 +0000 (00:19 +0200)
src/rrd2json.c
src/rrd2json.h
src/web_client.c
web/dashboard.html
web/dashboard.js

index 8969b324b1bf321be020a1569705887295aa4649..6539ace43461b6708dcedae4a1b79f11e8c3afa7 100755 (executable)
@@ -269,9 +269,6 @@ void rrd_stats_all_json(BUFFER *wb)
 typedef struct rrdresult {
        RRDSET *st;                     // the chart this result refers to
 
-       int group;                              // how many collected values were grouped for each row
-       int update_every;               // what is the suggested update frequency in seconds
-
        int d;                                  // the number of dimensions
        int n;                                  // the number of values in the arrays
        int rows;                       // the number of rows used
@@ -284,6 +281,15 @@ typedef struct rrdresult {
 
        int 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
+
+       calculated_number min;
+       calculated_number max;
+
+       time_t before;
+       time_t after;
+
        int has_st_lock;                // if st is read locked by us
 } RRDR;
 
@@ -370,6 +376,153 @@ void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
        }
 }
 
+void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
+{
+       switch(format) {
+       case DATASOURCE_JSON:
+               buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
+               break;
+
+       case DATASOURCE_DATATABLE_JSON:
+               buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
+               break;
+
+       case DATASOURCE_DATATABLE_JSONP:
+               buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
+               break;
+
+       case DATASOURCE_JSONP:
+               buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
+               break;
+
+       case DATASOURCE_SSV:
+               buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
+               break;
+
+       case DATASOURCE_CSV:
+               buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
+               break;
+
+       case DATASOURCE_TSV:
+               buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
+               break;
+
+       case DATASOURCE_HTML:
+               buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
+               break;
+
+       case DATASOURCE_JS_ARRAY:
+               buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
+               break;
+
+       case DATASOURCE_SSV_COMMA:
+               buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
+               break;
+
+       default:
+               buffer_strcat(wb, "unknown");
+               break;
+       }
+}
+
+void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
+{
+       char kq[2] = "",                                        // key quote
+               sq[2] = "";                                             // string quote
+
+       if( options & RRDR_OPTION_GOOGLE_JSON ) {
+               kq[0] = '\0';
+               sq[0] = '\'';
+       }
+       else {
+               kq[0] = '"';
+               sq[0] = '"';
+       }
+
+       buffer_sprintf(wb, "{\n"
+                       "       %sid%s: %s%s%s,\n"
+                       "       %sname%s: %s%s%s,\n"
+                       "       %supdate_every%s: %d,\n"
+                       "       %smin%s: "
+                       , kq, kq, sq, r->st->id, sq
+                       , kq, kq, sq, r->st->name, sq
+                       , kq, kq, r->update_every
+                       , kq, kq);
+
+       buffer_rrd_value(wb, r->min);
+       buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
+       buffer_rrd_value(wb, r->max);
+       buffer_sprintf(wb, ",\n"
+                       "       %sbefore%s: %u,\n"
+                       "       %safter%s: %u,\n"
+                       "       %sdimension_names%s: ["
+                       , kq, kq, r->before
+                       , kq, kq, r->after
+                       , kq, kq);
+
+
+       long c, i;
+       RRDDIM *rd;
+       for(c = 0, i = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
+               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+               if(i) buffer_strcat(wb, ", ");
+               buffer_strcat(wb, sq);
+               buffer_strcat(wb, rd->name);
+               buffer_strcat(wb, sq);
+               i++;
+       }
+       buffer_sprintf(wb, "],\n"
+                       "       %sdimension_ids%s: ["
+                       , kq, kq);
+
+       for(c = 0, i = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
+               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+               if(i) buffer_strcat(wb, ", ");
+               buffer_strcat(wb, sq);
+               buffer_strcat(wb, rd->id);
+               buffer_strcat(wb, sq);
+               i++;
+       }
+       buffer_sprintf(wb, "],\n"
+                       "       %sdimensions%s: %d,\n"
+                       "       %spoints%s: %d,\n"
+                       "       %sformat%s: %s"
+                       , kq, kq, i
+                       , kq, kq, rrdr_rows(r)
+                       , kq, kq, sq
+                       );
+
+       rrdr_buffer_print_format(wb, format);
+
+       buffer_sprintf(wb, "%s,\n"
+                       "       %sresult%s: "
+                       , sq
+                       , kq, kq
+                       );
+
+       if(string_value) buffer_strcat(wb, sq);
+}
+
+void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
+{
+       if(r) {;}
+       if(format) {;}
+
+       char sq[2] = "";                                                // string quote
+
+       if( options & RRDR_OPTION_GOOGLE_JSON )
+               sq[0] = '\'';
+       else
+               sq[0] = '"';
+
+       if(string_value) buffer_strcat(wb, sq);
+       buffer_strcat(wb, "\n}\n");
+}
+
 #define JSON_DATES_JS 1
 #define JSON_DATES_TIMESTAMP 2
 
@@ -414,7 +567,7 @@ static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
                snprintf(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
                snprintf(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
 
-               buffer_sprintf(wb, "{\n %supdate_every%s: %d,\n %scols%s:\n     [\n", kq, kq, r->update_every, kq, kq, kq, kq);
+               buffer_sprintf(wb, "{\n %scols%s:\n     [\n", kq, kq, kq, kq);
                buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
                buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
                buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
@@ -449,7 +602,7 @@ static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
                snprintf(data_begin, 100, "],\n %sdata%s:\n     [\n", kq, kq);
                snprintf(finish,     100, "\n   ]\n}\n");
 
-               buffer_sprintf(wb, "{\n %supdate_every%s: %d,\n %slabels%s: [", kq, kq, r->update_every, kq, kq);
+               buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
                buffer_sprintf(wb, "%stime%s", sq, sq);
        }
 
@@ -660,12 +813,12 @@ static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startlin
        }
 }
 
-static void rrdr2ssv(RRDR *r, BUFFER *out, uint32_t options, const char *prefix, const char *separator, const char *suffix)
+static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
 {
        long c, i;
        RRDDIM *d;
 
-       buffer_strcat(out, prefix);
+       buffer_strcat(wb, prefix);
        long start = 0, end = rrdr_rows(r), step = 1;
        if((options & RRDR_OPTION_REVERSED)) {
                start = rrdr_rows(r) - 1;
@@ -712,20 +865,20 @@ static void rrdr2ssv(RRDR *r, BUFFER *out, uint32_t options, const char *prefix,
                }
 
                if(likely(i != start))
-                       buffer_strcat(out, separator);
+                       buffer_strcat(wb, separator);
 
                if(all_null) {
                        if(options & RRDR_OPTION_NULL2ZERO)
-                               buffer_strcat(out, "0");
+                               buffer_strcat(wb, "0");
                        else
-                               buffer_strcat(out, "null");
+                               buffer_strcat(wb, "null");
                }
                else if(options & RRDR_OPTION_MIN2MAX)
-                       buffer_rrd_value(out, max - min);
+                       buffer_rrd_value(wb, max - min);
                else
-                       buffer_rrd_value(out, sum);
+                       buffer_rrd_value(wb, sum);
        }
-       buffer_strcat(out, suffix);
+       buffer_strcat(wb, suffix);
 }
 
 inline static calculated_number *rrdr_line_values(RRDR *r)
@@ -803,10 +956,11 @@ static RRDR *rrdr_create(RRDSET *st, int n)
                return NULL;
        }
 
-       RRDR *r = calloc(1, sizeof(RRDR));
+       RRDR *r = malloc(sizeof(RRDR));
        if(unlikely(!r)) goto cleanup;
 
        r->st = st;
+       r->has_st_lock = 0;
 
        rrdr_lock_rrdset(r);
 
@@ -815,21 +969,30 @@ static RRDR *rrdr_create(RRDSET *st, int n)
 
        r->n = n;
 
-       r->t = calloc(n, sizeof(time_t));
+       r->t = malloc(n * sizeof(time_t));
        if(unlikely(!r->t)) goto cleanup;
 
-       r->v = calloc(n * r->d, sizeof(calculated_number));
+       r->v = malloc(n * r->d * sizeof(calculated_number));
        if(unlikely(!r->v)) goto cleanup;
 
-       r->o = calloc(n * r->d, sizeof(uint8_t));
+       r->o = malloc(n * r->d * sizeof(uint8_t));
        if(unlikely(!r->o)) goto cleanup;
 
-       r->od = calloc(r->d, sizeof(uint8_t));
+       r->od = malloc(r->d * sizeof(uint8_t));
        if(unlikely(!r->od)) goto cleanup;
 
        r->c = -1;
        r->rows = 0;
 
+       r->group = 1;
+       r->update_every = 1;
+
+       r->min = 0.0;
+       r->max = 0.0;
+
+       r->before = 0;
+       r->after = 0;
+
        return r;
 
 cleanup:
@@ -1044,7 +1207,13 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                if(unlikely(now > before)) continue;
                if(unlikely(now < after)) break;
 
-               if(unlikely(group_count == 0)) group_start_t = now;
+               if(unlikely(group_count == 0)) {
+                       group_start_t = now;
+                       if(unlikely(!r->before)) {
+                               r->before = group_start_t;
+                               r->after = group_start_t;
+                       }
+               }
                group_count++;
 
                if(unlikely(group_count == group)) {
@@ -1086,6 +1255,8 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                if(unlikely(add_this)) {
                        if(unlikely(!rrdr_line_init(r, group_start_t))) break;
 
+                       r->after = group_start_t;
+
                        calculated_number *cn = rrdr_line_values(r);
                        uint8_t *co = rrdr_line_options(r);
 
@@ -1110,6 +1281,9 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                                        cn[c] = group_values[c];
                                }
 
+                               if(cn[c] < r->min) r->min = cn[c];
+                               if(cn[c] > r->max) r->max = cn[c];
+
                                // reset them for the next loop
                                group_values[c] = 0;
                                group_counts[c] = 0;
@@ -1121,82 +1295,160 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                        add_this = 0;
                }
        }
+       r->after += group * st->update_every;
 
        rrdr_done(r);
        //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
        return r;
 }
 
-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)
+int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp)
 {
-       RRDR *rrdr = rrd2rrdr(st, points, after, before, group_method);
-       if(!rrdr) {
-               buffer_strcat(out, "Cannot generate output with these parameters on this chart.");
+       RRDR *r = rrd2rrdr(st, points, after, before, group_method);
+       if(!r) {
+               buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
                return 500;
        }
 
        if(dimensions)
-               rrdr_disable_not_selected_dimensions(rrdr, buffer_tostring(dimensions));
+               rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
 
-       if(latest_timestamp && rrdr_rows(rrdr) > 0)
-               *latest_timestamp = rrdr->t[rrdr_rows(rrdr) - 1];
+       if(latest_timestamp && rrdr_rows(r) > 0)
+               *latest_timestamp = r->before;
 
        switch(format) {
        case DATASOURCE_SSV:
-               out->contenttype = CT_TEXT_PLAIN;
-               rrdr2ssv(rrdr, out, options, "", " ", "");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
+                       rrdr2ssv(r, wb, options, "", " ", "");
+                       rrdr_json_wrapper_end(r, wb, format, options, 1);
+               }
+               else {
+                       wb->contenttype = CT_TEXT_PLAIN;
+                       rrdr2ssv(r, wb, options, "", " ", "");
+               }
                break;
 
        case DATASOURCE_SSV_COMMA:
-               out->contenttype = CT_TEXT_PLAIN;
-               rrdr2ssv(rrdr, out, options, "", ",", "");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
+                       rrdr2ssv(r, wb, options, "", ",", "");
+                       rrdr_json_wrapper_end(r, wb, format, options, 1);
+               }
+               else {
+                       wb->contenttype = CT_TEXT_PLAIN;
+                       rrdr2ssv(r, wb, options, "", ",", "");
+               }
                break;
 
        case DATASOURCE_JS_ARRAY:
-               out->contenttype = CT_APPLICATION_JSON;
-               rrdr2ssv(rrdr, out, options, "[", ",", "]");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
+                       rrdr2ssv(r, wb, options, "[", ",", "]");
+                       rrdr_json_wrapper_end(r, wb, format, options, 0);
+               }
+               else {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr2ssv(r, wb, options, "[", ",", "]");
+               }
                break;
 
        case DATASOURCE_CSV:
-               out->contenttype = CT_TEXT_PLAIN;
-               rrdr2csv(rrdr, out, options, "", ",", "\r\n");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
+                       rrdr2csv(r, wb, options, "", ",", "\\n");
+                       rrdr_json_wrapper_end(r, wb, format, options, 1);
+               }
+               else {
+                       wb->contenttype = CT_TEXT_PLAIN;
+                       rrdr2csv(r, wb, options, "", ",", "\r\n");
+               }
                break;
 
        case DATASOURCE_TSV:
-               out->contenttype = CT_TEXT_PLAIN;
-               rrdr2csv(rrdr, out, options, "", "\t", "\r\n");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
+                       rrdr2csv(r, wb, options, "", "\t", "\\n");
+                       rrdr_json_wrapper_end(r, wb, format, options, 1);
+               }
+               else {
+                       wb->contenttype = CT_TEXT_PLAIN;
+                       rrdr2csv(r, wb, options, "", "\t", "\r\n");
+               }
                break;
 
        case DATASOURCE_HTML:
-               out->contenttype = CT_TEXT_HTML;
-               buffer_strcat(out, "<html>\n<center><table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">");
-               rrdr2csv(rrdr, out, options, "<tr><td>", "</td><td>", "</td></tr>\n");
-               buffer_strcat(out, "</table>\n</center>\n</html>\n");
+               if(options & RRDR_OPTION_JSON_WRAP) {
+                       wb->contenttype = CT_APPLICATION_JSON;
+                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
+                       buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
+                       rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n");
+                       buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
+                       rrdr_json_wrapper_end(r, wb, format, options, 1);
+               }
+               else {
+                       wb->contenttype = CT_TEXT_HTML;
+                       buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
+                       rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n");
+                       buffer_strcat(wb, "</table>\n</center>\n</html>\n");
+               }
                break;
 
        case DATASOURCE_DATATABLE_JSONP:
-               out->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-               rrdr2json(rrdr, out, options, 1);
+               wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+               rrdr2json(r, wb, options, 1);
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_end(r, wb, format, options, 0);
                break;
 
        case DATASOURCE_DATATABLE_JSON:
-               out->contenttype = CT_APPLICATION_JSON;
-               rrdr2json(rrdr, out, options, 1);
+               wb->contenttype = CT_APPLICATION_JSON;
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+               rrdr2json(r, wb, options, 1);
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_end(r, wb, format, options, 0);
                break;
 
        case DATASOURCE_JSONP:
-               out->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-               rrdr2json(rrdr, out, options, 0);
+               wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+               rrdr2json(r, wb, options, 0);
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_end(r, wb, format, options, 0);
                break;
 
        case DATASOURCE_JSON:
        default:
-               out->contenttype = CT_APPLICATION_JSON;
-               rrdr2json(rrdr, out, options, 0);
+               wb->contenttype = CT_APPLICATION_JSON;
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+               rrdr2json(r, wb, options, 0);
+
+               if(options & RRDR_OPTION_JSON_WRAP)
+                       rrdr_json_wrapper_end(r, wb, format, options, 0);
                break;
        }
 
-       rrdr_free(rrdr);
+       rrdr_free(r);
        return 200;
 }
 
index c4c49506546ebe7f2646776fd4f038a1ddc5e937..353145ab312092d80ed3938cf6f7bca288cb09f9 100755 (executable)
@@ -22,6 +22,17 @@ extern char *hostname;
 #define DATASOURCE_JS_ARRAY 8
 #define DATASOURCE_SSV_COMMA 9
 
+#define DATASOURCE_FORMAT_JSON "json"
+#define DATASOURCE_FORMAT_DATATABLE_JSON "datatable"
+#define DATASOURCE_FORMAT_DATATABLE_JSONP "datasource"
+#define DATASOURCE_FORMAT_JSONP "jsonp"
+#define DATASOURCE_FORMAT_SSV "ssv"
+#define DATASOURCE_FORMAT_CSV "csv"
+#define DATASOURCE_FORMAT_TSV "tsv"
+#define DATASOURCE_FORMAT_HTML "html"
+#define DATASOURCE_FORMAT_JS_ARRAY "array"
+#define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
+
 #define GROUP_AVERAGE  0
 #define GROUP_MAX              1
 #define GROUP_SUM              2
@@ -35,6 +46,7 @@ extern char *hostname;
 #define RRDR_OPTION_NULL2ZERO          0x00000040 // do not show nulls, convert them to zeros
 #define RRDR_OPTION_OBJECTSROWS                0x00000080 // each row of values should be an object, not an array
 #define RRDR_OPTION_GOOGLE_JSON                0x00000100 // comply with google JSON/JSONP specs
+#define RRDR_OPTION_JSON_WRAP          0x00000200 // wrap the response in a JSON header with info about the result
 
 extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
 extern void rrd_stats_api_v1_charts(BUFFER *wb);
index b38cff50941cff29cfcdcfe48b731e30a2cd5739..499b52c23e5819a3aeea2537ffce7d4cc4a5f5df 100755 (executable)
@@ -414,14 +414,16 @@ uint32_t web_client_api_request_v1_data_options(char *o)
                        ret |= RRDR_OPTION_NONZERO;
                else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
                        ret |= RRDR_OPTION_REVERSED;
-               else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
-                       ret |= RRDR_OPTION_ABSOLUTE;
+               else if(!strcmp(tok, "jsonwrap"))
+                       ret |= RRDR_OPTION_JSON_WRAP;
                else if(!strcmp(tok, "min2max"))
                        ret |= RRDR_OPTION_MIN2MAX;
-               else if(!strcmp(tok, "seconds"))
-                       ret |= RRDR_OPTION_SECONDS;
                else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
                        ret |= RRDR_OPTION_MILLISECONDS;
+               else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
+                       ret |= RRDR_OPTION_ABSOLUTE;
+               else if(!strcmp(tok, "seconds"))
+                       ret |= RRDR_OPTION_SECONDS;
                else if(!strcmp(tok, "null2zero"))
                        ret |= RRDR_OPTION_NULL2ZERO;
                else if(!strcmp(tok, "objectrows"))
@@ -435,39 +437,39 @@ uint32_t web_client_api_request_v1_data_options(char *o)
 
 int web_client_api_request_v1_data_format(char *name)
 {
-       if(!strcmp(name, "datatable"))
+       if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
                return DATASOURCE_DATATABLE_JSON;
 
-       else if(!strcmp(name, "datasource"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
                return DATASOURCE_DATATABLE_JSONP;
 
-       else if(!strcmp(name, "json"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
                return DATASOURCE_JSON;
 
-       else if(!strcmp(name, "jsonp"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
                return DATASOURCE_JSONP;
 
-       else if(!strcmp(name, "ssv"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
                return DATASOURCE_SSV;
 
-       else if(!strcmp(name, "csv"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
                return DATASOURCE_CSV;
 
-       else if(!strcmp(name, "tsv"))
-               return DATASOURCE_TSV;
-
-       else if(!strcmp(name, "tsv-excel"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_TSV)) // tsv
                return DATASOURCE_TSV;
 
-       else if(!strcmp(name, "html"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
                return DATASOURCE_HTML;
 
-       else if(!strcmp(name, "array"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
                return DATASOURCE_JS_ARRAY;
 
-       else if(!strcmp(name, "ssvcomma"))
+       else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
                return DATASOURCE_SSV_COMMA;
 
+       else if(!strcmp(name, "tsv-excel"))
+               return DATASOURCE_TSV;
+
        return DATASOURCE_JSON;
 }
 
index 8ed622d00cef2ebae2dfdc7462a0e317d1b26e88..a37c244b33f3109b97a1042853efbeae27040abe 100755 (executable)
@@ -17,7 +17,7 @@
 </head>
 <body>
 
-<h1>NetData Custom Dashboard</h1>
+<h1>NetData Custom Dashboard <div data-netdata="system.cpu" data-chart-library="sparkline" data-height="30" data-after="-600" data-sparkline-linecolor="#888"></div></h1>
 
 This is a template for building custom dashboards. To build a dashboard you just do this:
 
@@ -90,7 +90,7 @@ They also have mouse-hover support.
                data-chart-library="sparkline"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time101"
                ></div>
        <br/>
@@ -101,7 +101,7 @@ They also have mouse-hover support.
                data-chart-library="sparkline"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time102"
                ></div>
        <br/>
@@ -112,7 +112,7 @@ They also have mouse-hover support.
                data-chart-library="sparkline"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time103"
                ></div>
        <br/>
@@ -132,7 +132,7 @@ the total of all dimensions.
                data-chart-library="peity"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time001"
                ></div>
        <br/>
@@ -143,7 +143,7 @@ the total of all dimensions.
                data-chart-library="peity"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time002"
                ></div>
        <br/>
@@ -154,7 +154,7 @@ the total of all dimensions.
                data-chart-library="peity"
                data-width="100%"
                data-height="30"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time003"
                ></div>
        <br/>
@@ -175,7 +175,7 @@ For the moment zooming is just on the presentation layer (the data do not become
                data-chart-library="dygraph"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time201"
                ></div>
        <br/>
@@ -186,7 +186,7 @@ For the moment zooming is just on the presentation layer (the data do not become
                data-chart-library="dygraph"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time202"
                ></div>
        <br/>
@@ -197,7 +197,7 @@ For the moment zooming is just on the presentation layer (the data do not become
                data-chart-library="dygraph"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time203"
                ></div>
        <br/>
@@ -217,7 +217,7 @@ NetData is a complete Google Visualization API provider.
                data-chart-library="google"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time301"
                ></div>
        <br/>
@@ -228,7 +228,7 @@ NetData is a complete Google Visualization API provider.
                data-chart-library="google"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time302"
                ></div>
        <br/>
@@ -239,7 +239,7 @@ NetData is a complete Google Visualization API provider.
                data-chart-library="google"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time303"
                ></div>
        <br/>
@@ -260,7 +260,7 @@ Unfortunatelly, Morris Charts are veeeeeeeeeeeeeeeery slow! We force them to low
                data-chart-library="morris"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time401"
                ></div>
        <br/>
@@ -271,7 +271,7 @@ Unfortunatelly, Morris Charts are veeeeeeeeeeeeeeeery slow! We force them to low
                data-chart-library="morris"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time402"
                ></div>
        <br/>
@@ -282,7 +282,7 @@ Unfortunatelly, Morris Charts are veeeeeeeeeeeeeeeery slow! We force them to low
                data-chart-library="morris"
                data-width="100%"
                data-height="200"
-               data-after="-600"
+               data-after="-300"
                data-dt-element-name="time403"
                ></div>
        <br/>
index 9bc91c1c4983eb28fcf7004610baaeed336411d4..da8b1daff5fd5f7e44a87ad6b40b19d08ab11ca0 100755 (executable)
        NETDATA.options = {
                targets: null,                          
                updated_dom: 1,
-               debug: 0,
                last_paused: 0,
+               page_is_visible: 1,
 
                current: {
                        pixels_per_point: 1,
                        idle_between_charts: 100,
                        idle_between_loops: 500,
+                       idle_lost_focus: 500,
                        fast_render_timeframe: 200 // render continously for these many ms
+               },
+
+               debug: {
+                       show_boxes:             0,
+                       main_loop:                      0,
+                       focus:                          1,
+                       visibility:             0,
+                       chart_data_url:         1,
+                       chart_errors:           1,
+                       chart_timing:           0,
+                       chart_calls:            0,
                }
        }
 
-       if(NETDATA.options.debug) console.log('welcome to NETDATA');
+       if(NETDATA.options.debug.main_loop) console.log('welcome to NETDATA');
 
 
        // ----------------------------------------------------------------------------------------------------------------
                self = $(element);
 
                bgcolor = ""
-               if(NETDATA.options.debug)
+               if(NETDATA.options.debug.show_boxes)
                        bgcolor = " background-color: lightgrey;";
 
-               element.innerHTML = '<div style="overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
+               element.innerHTML = '<div style="font-size: xx-small; overflow: hidden;' + bgcolor + ' width: 100%; height: 100%;"><small>'
                        + message
                        + '</small></div>';
 
        }
 
        // ----------------------------------------------------------------------------------------------------------------
+       // Library functions
+
        // Load a script without jquery
        // This is used to load jquery - after it is loaded, we use jquery
-
        NETDATA._loadjQuery = function(callback) {
                if(typeof jQuery == 'undefined') {
                        var script = document.createElement('script');
                        callback();
        }
 
+       NETDATA.ColorLuminance = function(hex, lum) {
+               // validate hex string
+               hex = String(hex).replace(/[^0-9a-f]/gi, '');
+               if (hex.length < 6)
+                       hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
+
+               lum = lum || 0;
+
+               // convert to decimal and change luminosity
+               var rgb = "#", c, i;
+               for (i = 0; i < 3; i++) {
+                       c = parseInt(hex.substr(i*2,2), 16);
+                       c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
+                       rgb += ("00"+c).substr(c.length);
+               }
+
+               return rgb;
+       }
+
+       NETDATA.guid = function() {
+               function s4() {
+                       return Math.floor((1 + Math.random()) * 0x10000)
+                                       .toString(16)
+                                       .substring(1);
+                       }
+
+                       return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+       }
+
+       // this is the main function - where everything starts
+       NETDATA.init = function() {
+               // this should be called only once
+
+               $(window).blur(function() {
+                       NETDATA.options.page_is_visible = 0;
+                       if(NETDATA.options.debug.focus) console.log('Lost Focus!');
+               });
+
+               $(window).focus(function() {
+                       NETDATA.options.page_is_visible = 1;
+                       if(NETDATA.options.debug.focus) console.log('Focus restored!');
+               });
+
+               NETDATA.getDomCharts(function() {
+                       NETDATA.chartRefresher(0);
+               });
+       }
+
+       // user function to signal us the DOM has been
+       // updated.
+       NETDATA.updatedDom = function() {
+               NETDATA.options.updated_dom = 1;
+       }
+
+       // ----------------------------------------------------------------------------------------------------------------
+
        NETDATA.generateChartDataURL = function() {
                self = $(this);
 
                        pixels_per_point = NETDATA.options.current.pixels_per_point
 
                var points = self.data('points') || Math.round(width / pixels_per_point);
+               var format = self.data('format') || NETDATA.chartLibraries[library].format;
+               var options = self.data('options') || NETDATA.chartLibraries[library].options;
 
                // build the data URL
                var url = host + chart.data_url;
-               url += "&format="  + NETDATA.chartLibraries[library].format;
+               url += "&format="  + format;
                url += "&points="  + points.toString();
-               url += "&options=" + NETDATA.chartLibraries[library].options;
+               url += "&options=" + options;
                url += "&group="   + method;
 
                if(after)
                        .data('calculated-points', points)
                        .data('calculated-url', url);
 
-               if(NETDATA.options.debug) console.log('generateChartDataURL(): ' + url + ' WxH:' + width + 'x' + height + ' points: ' + points + ' library: ' + library);
+               if(NETDATA.options.debug.chart_data_url) console.log('generateChartDataURL(): ' + url + ' WxH:' + width + 'x' + height + ' points: ' + points + ' library: ' + library);
                return url;
        }
 
        NETDATA.validateDomCharts = function(targets, index, callback) {
-               if(NETDATA.options.debug) console.log('validateDomCharts() working on ' + index);
+               if(NETDATA.options.debug.main_loop) console.log('validateDomCharts() working on ' + index);
 
                var target = targets.get(index);
                if(target == null) {
-                       if(NETDATA.options.debug) console.log('validateDomCharts(): all ' + (index - 1) + ' charts parsed.');
+                       if(NETDATA.options.debug.main_loop) console.log('validateDomCharts(): all ' + (index - 1) + ' charts parsed.');
                        if(typeof callback == 'function') callback();
                }
                else {
                                var host = self.data('host') || NETDATA.chartDefaults.host;
                                var library = self.data('chart-library') || NETDATA.chartDefaults.library;
 
-                               if(NETDATA.options.debug) console.log('validateDomCharts() parsing ' + id + ' of type ' + library);
+                               if(NETDATA.options.debug.main_loop) console.log('validateDomCharts() parsing ' + id + ' of type ' + library);
 
                                if(typeof NETDATA.chartLibraries[library] == 'undefined') {
                                        NETDATA.error(402, library);
        NETDATA.sizeDomCharts = function(targets, index, callback) {
                // this is used to quickly size all charts to their size
 
-               if(NETDATA.options.debug) console.log('sizeDomCharts() working on ' + index);
+               if(NETDATA.options.debug.main_loop) console.log('sizeDomCharts() working on ' + index);
 
                var target = targets.get(index);
                if(target == null) {
-                       if(NETDATA.options.debug) console.log('sizeDomCharts(): all ' + (index - 1) + ' charts sized.');
+                       if(NETDATA.options.debug.main_loop) console.log('sizeDomCharts(): all ' + (index - 1) + ' charts sized.');
                        if(typeof callback == 'function') callback();
                }
                else {
                NETDATA.options.targets = $('div[data-netdata]').filter(':visible')
                        .bind('create', function(event, data) {
                                var self = $(this);
-                               
-                               if(NETDATA.options.debug)
+
+                               if(NETDATA.options.debug.chart_errors)
                                        NETDATA.chartLibraries[self.data('chart-library')].create(this, data);
                                else {
                                        try {
                        })
                        .bind('update', function(event, data) {
                                var self = $(this);
-                               if(NETDATA.options.debug)
+                               if(NETDATA.options.debug.chart_errors)
                                        NETDATA.chartLibraries[self.data('chart-library')].update(this, data);
                                else {
                                        try {
                                }
                        });
 
-               if(NETDATA.options.debug)
+               if(NETDATA.options.debug.main_loop)
                        console.log('DOM updated - there are ' + NETDATA.options.targets.length + ' charts on page.');
 
                NETDATA.sizeDomCharts(NETDATA.options.targets, 0, function() {
                });
        }
 
-       NETDATA.init = function() {
-               // this should be called only once
-               NETDATA.getDomCharts(function() {
-                       NETDATA.chartRefresher(0);
-               });
-       }
-
        // ----------------------------------------------------------------------------------------------------------------
 
        //var chart = function() {
                // check if this chart has to be refreshed now
                var now = new Date().getTime();
                if(last + every > now) {
-                       console.log(self.data('netdata') + ' too soon - skipping.');
+                       if(NETDATA.options.debug.chart_timing) console.log(self.data('netdata') + ' too soon - skipping.');
                        if(typeof callback == 'function') callback();
                }
                else if(!self.visible(true)) {
-                       console.log(self.data('netdata') + ' is NOT visible.');
+                       if(NETDATA.options.debug.visibility) console.log(self.data('netdata') + ' is NOT visible.');
                        if(typeof callback == 'function') callback();
                }
                else {
-                       console.log(self.data('netdata') + ' is visible, downloading data...');
+                       if(NETDATA.options.debug.visibility) console.log(self.data('netdata') + ' is visible, downloading data...');
                        $.ajax( {
                                url: NETDATA.generateChartDataURL.call(element), // self.data('chart-url'),
                                crossDomain: true
                        .then(function(data) {
                                var started = new Date().getTime();
 
+                               // if the result is JSON, find the latest update-every
+                               if(NETDATA.chartLibraries[self.data('chart-library')].jsonWrapper &&
+                                       typeof data.update_every != 'undefined')
+                                               self.data('update-every', data.update_every * 1000);
+
                                if(self.data('created')) {
-                                       console.log('updating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
+                                       if(NETDATA.options.debug.chart_calls) console.log('updating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
                                        self.trigger('update', [data]);
                                        // NETDATA.chartLibraries[self.data('chart-library')].update(element, data);
                                }
                                else {
-                                       console.log('creating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
+                                       if(NETDATA.options.debug.chart_calls) console.log('creating ' + self.data('chart-library') + ' chart ' + self.data('netdata'));
                                        self.trigger('create', [data]);
                                        //NETDATA.chartLibraries[self.data('chart-library')].create(element, data);
                                        self.data('created', true);
        };
 
        NETDATA.chartRefresher = function(index) {
-               // if(NETDATA.options.debug) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
+               // if(NETDATA.options.debug.mail_loop) console.log('NETDATA.chartRefresher(<targets, ' + index + ')');
 
-               now = new Date().getTime();
-
-               if(NETDATA.options.updated_dom) {
-                       NETDATA.getDomCharts(function() {
-                               NETDATA.chartRefresher(0);
-                       });
+               if(!NETDATA.options.page_is_visible) {
+                       if(NETDATA.options.debug.main_loop) console.log('waiting focus...');
+                       setTimeout(function() {
+                                       NETDATA.chartRefresher(index);
+                               }, NETDATA.options.current.idle_lost_focus);
                }
                else {
-                       var target = NETDATA.options.targets.get(index);
-                       if(target == null) {
-                               if(NETDATA.options.debug) console.log('waiting to restart main loop...');
-                               NETDATA.options.last_paused = now;
+                       now = new Date().getTime();
 
-                               setTimeout(function() {
+                       if(NETDATA.options.updated_dom) {
+                               // the dom has been updated
+                               // get the dom parts again
+                               NETDATA.getDomCharts(function() {
                                        NETDATA.chartRefresher(0);
-                               }, NETDATA.options.current.idle_between_loops);
+                               });
                        }
                        else {
-                               var self = $(target);
-                               if(!self.data('enabled')) {
-                                       NETDATA.chartRefresher(++index);
+                               var target = NETDATA.options.targets.get(index);
+                               if(target == null) {
+                                       if(NETDATA.options.debug.main_loop) console.log('waiting to restart main loop...');
+                                       NETDATA.options.last_paused = now;
+
+                                       setTimeout(function() {
+                                               NETDATA.chartRefresher(0);
+                                       }, NETDATA.options.current.idle_between_loops);
                                }
                                else {
-                                       if(now - NETDATA.options.last_paused < NETDATA.options.current.fast_render_timeframe) {
-                                               if(NETDATA.options.debug) console.log('fast rendering...');
-
-                                               NETDATA.chartValuesDownloader(target, function() {
-                                                       NETDATA.chartRefresher(++index);
-                                               });
+                                       var self = $(target);
+                                       if(!self.data('enabled')) {
+                                               NETDATA.chartRefresher(++index);
                                        }
                                        else {
-                                               if(NETDATA.options.debug) console.log('waiting for next refresh...');
-                                               NETDATA.options.last_paused = now;
+                                               if(now - NETDATA.options.last_paused < NETDATA.options.current.fast_render_timeframe) {
+                                                       if(NETDATA.options.debug.main_loop) console.log('fast rendering...');
 
-                                               setTimeout(function() {
                                                        NETDATA.chartValuesDownloader(target, function() {
                                                                NETDATA.chartRefresher(++index);
                                                        });
-                                               }, NETDATA.options.current.idle_between_charts);
+                                               }
+                                               else {
+                                                       if(NETDATA.options.debug.main_loop) console.log('waiting for next refresh...');
+                                                       NETDATA.options.last_paused = now;
+
+                                                       setTimeout(function() {
+                                                               NETDATA.chartValuesDownloader(target, function() {
+                                                                       NETDATA.chartRefresher(++index);
+                                                               });
+                                                       }, NETDATA.options.current.idle_between_charts);
+                                               }
                                        }
                                }
                        }
                }
        }
 
-       NETDATA.guid = function() {
-               function s4() {
-                       return Math.floor((1 + Math.random()) * 0x10000)
-                                       .toString(16)
-                                       .substring(1);
-                       }
-
-                       return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
-       }
-
        // ----------------------------------------------------------------------------------------------------------------
        // peity
 
                var self = $(element);
                var instance = self.data('peity-instance');
                var ins = $(instance);
-               ins.html(data);
+               ins.html(data.result);
 
                // peity.change() does not accept options
                // to pass width and height
                var self = $(element);
 
                var uuid = NETDATA.guid();
-               element.innerHTML = '<div id="' + uuid + '">' + data + '</div>';
+               element.innerHTML = '<div id="' + uuid + '">' + data.result + '</div>';
                var instance = document.getElementById(uuid);
                var ins = $(instance);
 
                var options = self.data('sparkline-options');
                options.width = self.data('calculated-width');
                options.height = self.data('calculated-height');
-               self.sparkline(data, options);
+               self.sparkline(data.result, options);
        }
 
        NETDATA.sparklineChartCreate = function(element, data) {
                var self = $(element);
                var chart = self.data('chart');
                var type = self.data('sparkline-type') || 'line';
-               var lineColor = self.data('sparkline-lineColor') || undefined;
-               var fillColor = self.data('sparkline-fillColor') || (chart.chart_type == 'line')?'#FFF':undefined;
-               var chartRangeMin = self.data('sparkline-chartRangeMin') || undefined;
-               var chartRangeMax = self.data('sparkline-chartRangeMax') || undefined;
+               var lineColor = self.data('sparkline-linecolor') || NETDATA.colors[0];
+               var fillColor = self.data('sparkline-fillcolor') || (chart.chart_type == 'line')?'#FFF':NETDATA.ColorLuminance(lineColor, 0.8);
+               var chartRangeMin = self.data('sparkline-chartrangemin') || undefined;
+               var chartRangeMax = self.data('sparkline-chartrangemax') || undefined;
                var composite = self.data('sparkline-composite') || undefined;
-               var enableTagOptions = self.data('sparkline-enableTagOptions') || undefined;
-               var tagOptionPrefix = self.data('sparkline-tagOptionPrefix') || undefined;
-               var tagValuesAttribute = self.data('sparkline-tagValuesAttribute') || undefined;
-               var disableHiddenCheck = self.data('sparkline-disableHiddenCheck') || undefined;
-               var defaultPixelsPerValue = self.data('sparkline-defaultPixelsPerValue') || undefined;
-               var spotColor = self.data('sparkline-spotColor') || undefined;
-               var minSpotColor = self.data('sparkline-minSpotColor') || undefined;
-               var maxSpotColor = self.data('sparkline-maxSpotColor') || undefined;
-               var spotRadius = self.data('sparkline-spotRadius') || undefined;
-               var valueSpots = self.data('sparkline-valueSpots') || undefined;
-               var highlightSpotColor = self.data('sparkline-highlightSpotColor') || undefined;
-               var highlightLineColor = self.data('sparkline-highlightLineColor') || undefined;
-               var lineWidth = self.data('sparkline-lineWidth') || undefined;
-               var normalRangeMin = self.data('sparkline-normalRangeMin') || undefined;
-               var normalRangeMax = self.data('sparkline-normalRangeMax') || undefined;
-               var drawNormalOnTop = self.data('sparkline-drawNormalOnTop') || undefined;
+               var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined;
+               var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined;
+               var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined;
+               var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined;
+               var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined;
+               var spotColor = self.data('sparkline-spotcolor') || undefined;
+               var minSpotColor = self.data('sparkline-minspotcolor') || undefined;
+               var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined;
+               var spotRadius = self.data('sparkline-spotradius') || undefined;
+               var valueSpots = self.data('sparkline-valuespots') || undefined;
+               var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined;
+               var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined;
+               var lineWidth = self.data('sparkline-linewidth') || undefined;
+               var normalRangeMin = self.data('sparkline-normalrangemin') || undefined;
+               var normalRangeMax = self.data('sparkline-normalrangemax') || undefined;
+               var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
                var xvalues = self.data('sparkline-xvalues') || undefined;
-               var chartRangeClip = self.data('sparkline-chartRangeClip') || undefined;
+               var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
                var xvalues = self.data('sparkline-xvalues') || undefined;
-               var chartRangeMinX = self.data('sparkline-chartRangeMinX') || undefined;
-               var chartRangeMaxX = self.data('sparkline-chartRangeMaxX') || undefined;
-               var disableInteraction = self.data('sparkline-disableInteraction') || false;
-               var disableTooltips = self.data('sparkline-disableTooltips') || false;
-               var disableHighlight = self.data('sparkline-disableHighlight') || false;
-               var highlightLighten = self.data('sparkline-highlightLighten') || 1.4;
-               var highlightColor = self.data('sparkline-highlightColor') || undefined;
-               var tooltipContainer = self.data('sparkline-tooltipContainer') || undefined;
-               var tooltipClassname = self.data('sparkline-tooltipClassname') || undefined;
-               var tooltipFormat = self.data('sparkline-tooltipFormat') || undefined;
-               var tooltipPrefix = self.data('sparkline-tooltipPrefix') || undefined;
-               var tooltipSuffix = self.data('sparkline-tooltipSuffix') || ' ' + chart.units;
-               var tooltipSkipNull = self.data('sparkline-tooltipSkipNull') || true;
-               var tooltipValueLookups = self.data('sparkline-tooltipValueLookups') || undefined;
-               var tooltipFormatFieldlist = self.data('sparkline-tooltipFormatFieldlist') || undefined;
-               var tooltipFormatFieldlistKey = self.data('sparkline-tooltipFormatFieldlistKey') || undefined;
-               var numberFormatter = self.data('sparkline-numberFormatter') || function(n){ return n.toFixed(2); };
-               var numberDigitGroupSep = self.data('sparkline-numberDigitGroupSep') || undefined;
-               var numberDecimalMark = self.data('sparkline-numberDecimalMark') || undefined;
-               var numberDigitGroupCount = self.data('sparkline-numberDigitGroupCount') || undefined;
-               var animatedZooms = self.data('sparkline-animatedZooms') || false;
+               var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
+               var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
+               var disableInteraction = self.data('sparkline-disableinteraction') || false;
+               var disableTooltips = self.data('sparkline-disabletooltips') || false;
+               var disableHighlight = self.data('sparkline-disablehighlight') || false;
+               var highlightLighten = self.data('sparkline-highlightlighten') || 1.4;
+               var highlightColor = self.data('sparkline-highlightcolor') || undefined;
+               var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined;
+               var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined;
+               var tooltipFormat = self.data('sparkline-tooltipformat') || undefined;
+               var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined;
+               var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + chart.units;
+               var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true;
+               var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined;
+               var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined;
+               var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined;
+               var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); };
+               var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined;
+               var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined;
+               var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined;
+               var animatedZooms = self.data('sparkline-animatedzooms') || false;
 
                var options = {
                        type: type,
                element.innerHTML = '<div style="display: inline-block; position: relative;" id="' + uuid + '"></div>';
                var div = document.getElementById(uuid);
 
-               self.sparkline(data, options);
+               self.sparkline(data.result, options);
                self.data('sparkline-options', options)
-               .data('uuid', uuid)
-               .data('created', true);
+                       .data('uuid', uuid)
+                       .data('created', true);
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                var self = $(element);
                var dygraph = self.data('dygraph-instance');
 
-               if(typeof data.update_every != 'undefined')
-                       self.data('update-every', data.update_every * 1000);
-
-               if(dygraph != null) {
-                       console.log('updating dygraphs');
-                       dygraph.updateOptions({
-                               file: data.data,
-                               labels: data.labels,
-                               labelsDivWidth: self.width() - 70
-                       });
-               }
-               else
-                       console.log('not updating dygraphs');
+               dygraph.updateOptions({
+                       file: data.result.data,
+                       labels: data.result.labels,
+                       labelsDivWidth: self.width() - 70
+               });
        };
 
        NETDATA.dygraphChartCreate = function(element, data) {
                var self = $(element);
                var chart = self.data('chart');
                var title = self.data('dygraph-title') || chart.title;
-               var titleHeight = self.data('dygraph-titleHeight') || 20;
-               var labelsDiv = self.data('dygraph-labelsDiv') || undefined;
-               var connectSeparatedPoints = self.data('dygraph-connectSeparatedPoints') || false;
-               var yLabelWidth = self.data('dygraph-yLabelWidth') || 12;
-               var stackedGraph = self.data('dygraph-stackedGraph') || (chart.chart_type == 'stacked')?true:false;
-               var stackedGraphNaNFill = self.data('dygraph-stackedGraphNaNFill') || 'none';
-               var hideOverlayOnMouseOut = self.data('dygraph-hideOverlayOnMouseOut') || true;
-               var fillGraph = self.data('dygraph-fillGraph') || (chart.chart_type == 'area')?true:false;
-               var drawPoints = self.data('dygraph-drawPoints') || false;
-               var labelsDivStyles = self.data('dygraph-labelsDivStyles') || { 'fontSize':'10px' };
-               // var labelsDivWidth = self.data('dygraph-labelsDivWidth') || 250;
-               var labelsDivWidth = self.width() - 70;
-               var labelsSeparateLines = self.data('dygraph-labelsSeparateLines') || false;
-               var labelsShowZeroValues = self.data('dygraph-labelsShowZeroValues') || true;
+               var titleHeight = self.data('dygraph-titleheight') || 20;
+               var labelsDiv = self.data('dygraph-labelsdiv') || undefined;
+               var connectSeparatedPoints = self.data('dygraph-connectseparatedpoints') || false;
+               var yLabelWidth = self.data('dygraph-ylabelwidth') || 12;
+               var stackedGraph = self.data('dygraph-stackedgraph') || (chart.chart_type == 'stacked')?true:false;
+               var stackedGraphNaNFill = self.data('dygraph-stackedgraphnanfill') || 'none';
+               var hideOverlayOnMouseOut = self.data('dygraph-hideoverlayonmouseout') || true;
+               var fillGraph = self.data('dygraph-fillgraph') || (chart.chart_type == 'area')?true:false;
+               var drawPoints = self.data('dygraph-drawpoints') || false;
+               var labelsDivStyles = self.data('dygraph-labelsdivstyles') || { 'fontSize':'10px' };
+               var labelsDivWidth = self.data('dygraph-labelsdivwidth') || self.width() - 70;
+               var labelsSeparateLines = self.data('dygraph-labelsseparatelines') || false;
+               var labelsShowZeroValues = self.data('dygraph-labelsshowzerovalues') || true;
                var legend = self.data('dygraph-legend') || 'onmouseover';
-               var showLabelsOnHighlight = self.data('dygraph-showLabelsOnHighlight') || true;
-               var gridLineColor = self.data('dygraph-gridLineColor') || '#EEE';
-               var axisLineColor = self.data('dygraph-axisLineColor') || '#EEE';
-               var maxNumberWidth = self.data('dygraph-maxNumberWidth') || 8;
-               var sigFigs = self.data('dygraph-sigFigs') || null;
-               var digitsAfterDecimal = self.data('dygraph-digitsAfterDecimal') || 2;
-               var axisLabelFontSize = self.data('dygraph-axisLabelFontSize') || 10;
-               var axisLineWidth = self.data('dygraph-axisLineWidth') || 0.3;
-               var drawAxis = self.data('dygraph-drawAxis') || true;
-               var strokeWidth = self.data('dygraph-strokeWidth') || 1.0;
-               var drawGapEdgePoints = self.data('dygraph-drawGapEdgePoints') || true;
+               var showLabelsOnHighlight = self.data('dygraph-showlabelsonhighlight') || true;
+               var gridLineColor = self.data('dygraph-gridlinecolor') || '#EEE';
+               var axisLineColor = self.data('dygraph-axislinecolor') || '#EEE';
+               var maxNumberWidth = self.data('dygraph-maxnumberwidth') || 8;
+               var sigFigs = self.data('dygraph-sigfigs') || null;
+               var digitsAfterDecimal = self.data('dygraph-digitsafterdecimal') || 2;
+               var axisLabelFontSize = self.data('dygraph-axislabelfontsize') || 10;
+               var axisLineWidth = self.data('dygraph-axislinewidth') || 0.3;
+               var drawAxis = self.data('dygraph-drawaxis') || true;
+               var strokeWidth = self.data('dygraph-strokewidth') || 1.0;
+               var drawGapEdgePoints = self.data('dygraph-drawgapedgepoints') || true;
                var colors = self.data('dygraph-colors') || NETDATA.colors;
-               var pointSize = self.data('dygraph-pointSize') || 1;
-               var stepPlot = self.data('dygraph-stepPlot') || false;
-               var strokeBorderColor = self.data('dygraph-strokeBorderColor') || 'white';
-               var strokeBorderWidth = self.data('dygraph-strokeBorderWidth') || (chart.chart_type == 'stacked')?1.0:0.0;
-               var strokePattern = self.data('dygraph-strokePattern') || undefined;
-               var highlightCircleSize = self.data('dygraph-highlightCircleSize') || 3;
-               var highlightSeriesOpts = self.data('dygraph-highlightSeriesOpts') || { strokeWidth: 1.5 };
-               var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightSeriesBackgroundAlpha') || (chart.chart_type == 'stacked')?0.7:0.5;
-               var pointClickCallback = self.data('dygraph-pointClickCallback') || undefined;
-               var showRangeSelector = self.data('dygraph-showRangeSelector') || false;
-               var showRoller = self.data('dygraph-showRoller') || false;
-               var valueFormatter = self.data('dygraph-valueFormatter') || undefined; //function(x){ return x.toFixed(2); };
-               var rightGap = self.data('dygraph-rightGap') || 5;
-               var drawGrid = self.data('dygraph-drawGrid') || true;
-               var drawXGrid = self.data('dygraph-drawXGrid') || undefined;
-               var drawYGrid = self.data('dygraph-drawYGrid') || undefined;
-               var gridLinePattern = self.data('dygraph-gridLinePattern') || null;
-               var gridLineWidth = self.data('dygraph-gridLineWidth') || 0.3;
+               var pointSize = self.data('dygraph-pointsize') || 1;
+               var stepPlot = self.data('dygraph-stepplot') || false;
+               var strokeBorderColor = self.data('dygraph-strokebordercolor') || 'white';
+               var strokeBorderWidth = self.data('dygraph-strokeborderwidth') || (chart.chart_type == 'stacked')?1.0:0.0;
+               var strokePattern = self.data('dygraph-strokepattern') || undefined;
+               var highlightCircleSize = self.data('dygraph-highlightcirclesize') || 3;
+               var highlightSeriesOpts = self.data('dygraph-highlightseriesopts') || { strokeWidth: 1.5 };
+               var highlightSeriesBackgroundAlpha = self.data('dygraph-highlightseriesbackgroundalpha') || (chart.chart_type == 'stacked')?0.7:0.5;
+               var pointClickCallback = self.data('dygraph-pointclickcallback') || undefined;
+               var showRangeSelector = self.data('dygraph-showrangeselector') || false;
+               var showRoller = self.data('dygraph-showroller') || false;
+               var valueFormatter = self.data('dygraph-valueformatter') || undefined; //function(x){ return x.toFixed(2); };
+               var rightGap = self.data('dygraph-rightgap') || 5;
+               var drawGrid = self.data('dygraph-drawgrid') || true;
+               var drawXGrid = self.data('dygraph-drawxgrid') || undefined;
+               var drawYGrid = self.data('dygraph-drawygrid') || undefined;
+               var gridLinePattern = self.data('dygraph-gridlinepattern') || null;
+               var gridLineWidth = self.data('dygraph-gridlinewidth') || 0.3;
 
                var options = {
                        title: title,
                        showRoller: showRoller,
                        valueFormatter: valueFormatter,
                        rightGap: rightGap,
-                       labels: data.labels,
+                       labels: data.result.labels,
                        axes: {
                                x: {
                                        pixelsPerLabel: 50,
                self.html('<div id="' + uuid + '" style="width: 100%; height: 100%;"></div>');
 
                var dchart = new Dygraph(document.getElementById(uuid),
-                       data.data, options);
+                       data.result.data, options);
 
                self.data('dygraph-instance', dchart)
                        .data('dygraph-options', options)
                var self = $(element);
                var morris = self.data('morris-instance');
 
-               if(typeof data.update_every != 'undefined')
-                       self.data('update-every', data.update_every * 1000);
-
                if(morris != null) {
                        console.log('updating morris');
-                       morris.setData(data.data);
+                       morris.setData(data.result.data);
                }
                else
                        console.log('not updating morris');
                var uuid = NETDATA.guid();
                self.html('<div id="' + uuid + '" style="width: ' + self.data('calculated-width') + 'px; height: ' + self.data('calculated-height') + 'px;"></div>');
 
-               // remove the 'time' element from the labels
-               data.labels.splice(0, 1);
-
                var options = {
                                element: uuid,
-                               data: data.data,
+                               data: data.result.data,
                                xkey: 'time',
-                               ykeys: data.labels,
-                               labels: data.labels,
+                               ykeys: data.dimension_names,
+                               labels: data.dimension_names,
                                lineWidth: 2,
                                pointSize: 2,
                                smooth: true,
                        morris = new Morris.Area(options);
 
                self.data('morris-instance', morris)
-               .data('created', true);
+                       .data('created', true);
        };
 
        // ----------------------------------------------------------------------------------------------------------------
                var gchart = self.data('google-instance');
                var options = self.data('google-options');
 
-               if(typeof data.update_every != 'undefined')
-                       self.data('update-every', data.update_every * 1000);
-
-               var datatable = new google.visualization.DataTable(data);
+               var datatable = new google.visualization.DataTable(data.result);
 
                gchart.draw(datatable, options);
        };
                var self = $(element);
                var chart = self.data('chart');
 
-               var datatable = new google.visualization.DataTable(data);
+               var datatable = new google.visualization.DataTable(data.result);
                var gchart;
 
                var options = {
                        initialized: false,
                        enabled: true,
                        format: 'json',
-                       options: 'ms|flip',
+                       options: 'ms|flip|jsonwrap',
+                       jsonWrapper: true,
                        pixels_per_point: 2,
                        detects_dimensions_on_update: false
                },
                        initialized: false,
                        enabled: true,
                        format: 'array',
-                       options: 'flip|min2max',
+                       options: 'flip|min2max|jsonwrap',
+                       jsonWrapper: true,
                        pixels_per_point: 2,
                        detects_dimensions_on_update: false
                },
                        initialized: false,
                        enabled: true,
                        format: 'ssvcomma',
-                       options: 'null2zero|flip|min2max',
+                       options: 'null2zero|flip|min2max|jsonwrap',
+                       jsonWrapper: true,
                        pixels_per_point: 2,
                        detects_dimensions_on_update: false
                },
                        initialized: false,
                        enabled: true,
                        format: 'json',
-                       options: 'objectrows|ms',
+                       options: 'objectrows|ms|jsonwrap',
+                       jsonWrapper: true,
                        pixels_per_point: 10,
                        detects_dimensions_on_update: false
                },
                        initialized: false,
                        enabled: true,
                        format: 'datatable',
-                       options: '',
+                       options: 'jsonwrap',
+                       jsonWrapper: true,
                        pixels_per_point: 2,
                        detects_dimensions_on_update: true
                },
                        enabled: true,
                        format: 'json',
                        options: '',
+                       jsonWrapper: false,
                        pixels_per_point: 1,
                        detects_dimensions_on_update: false
                }