├── test
├── data
│ ├── load.rrd
│ ├── cpu-idle.rrd
│ ├── cpu-nice.rrd
│ ├── cpu-steal.rrd
│ ├── cpu-user.rrd
│ ├── cpu-wait.rrd
│ ├── cpu-softirq.rrd
│ ├── cpu-system.rrd
│ ├── memory-free.rrd
│ ├── memory-used.rrd
│ ├── cpu-interrupt.rrd
│ ├── if_octets-eth0.rrd
│ ├── memory-cached.rrd
│ └── memory-buffered.rrd
├── test5.html
├── test6.html
├── test2.html
├── test3.html
├── test1.html
└── test4.html
├── README.md
└── js
├── sprintf.js
├── Color.js
├── base64.js
├── strftime.js
├── RrdDataFile.js
├── RrdGfxCanvas.js
├── RrdGfxSvg.js
├── binaryXHR.js
├── rrdFile.js
├── RrdTime.js
├── RrdRpn.js
├── RrdJson.js
└── RrdCmdLine.js
/test/data/load.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/load.rrd
--------------------------------------------------------------------------------
/test/data/cpu-idle.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-idle.rrd
--------------------------------------------------------------------------------
/test/data/cpu-nice.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-nice.rrd
--------------------------------------------------------------------------------
/test/data/cpu-steal.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-steal.rrd
--------------------------------------------------------------------------------
/test/data/cpu-user.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-user.rrd
--------------------------------------------------------------------------------
/test/data/cpu-wait.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-wait.rrd
--------------------------------------------------------------------------------
/test/data/cpu-softirq.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-softirq.rrd
--------------------------------------------------------------------------------
/test/data/cpu-system.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-system.rrd
--------------------------------------------------------------------------------
/test/data/memory-free.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/memory-free.rrd
--------------------------------------------------------------------------------
/test/data/memory-used.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/memory-used.rrd
--------------------------------------------------------------------------------
/test/data/cpu-interrupt.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/cpu-interrupt.rrd
--------------------------------------------------------------------------------
/test/data/if_octets-eth0.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/if_octets-eth0.rrd
--------------------------------------------------------------------------------
/test/data/memory-cached.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/memory-cached.rrd
--------------------------------------------------------------------------------
/test/data/memory-buffered.rrd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manuelluis/jsrrdgraph/HEAD/test/data/memory-buffered.rrd
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #jsrrdgraph
2 |
3 | This is a translation of the C source code of rrdtool to javascript in an attempt to reproduce the command ``rrdtool graph`` in javascript.
4 |
5 | You can view some test pages in: http://manuelluis.github.io/jsrrdgraph/
6 |
7 | The examples uses ``javascriptRRD`` for testing purposes, to load the data.
8 |
--------------------------------------------------------------------------------
/js/sprintf.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | **/
18 |
19 | "use strict";
20 |
21 | function sprintf()
22 | {
23 | var argc = 0;
24 | var args = arguments;
25 | var fmt = args[argc++];
26 |
27 | function lpad (str, padString, length)
28 | {
29 | while (str.length < length)
30 | str = padString + str;
31 | return str;
32 | }
33 |
34 | function format (match, width, dot, precision, length, conversion)
35 | {
36 | if (match === '%%') return '%';
37 |
38 | var value = args[argc++];
39 | var prefix;
40 |
41 | if (width === undefined)
42 | width = 0;
43 | else
44 | width = +width;
45 |
46 | if (precision === undefined)
47 | precision = conversion == 'd' ? 0 : 6;
48 | else
49 | precision = +precision;
50 |
51 | switch (conversion) {
52 | case 's':
53 | case 'c':
54 | return value;
55 | case 'd':
56 | return parseInt(value, 10);
57 | case 'e':
58 | prefix = value < 0 ? '-' : '';
59 | return lpad(prefix+Math.abs(value).toExponential(precision),' ',width);
60 | case 'F':
61 | case 'f':
62 | prefix = value < 0 ? '-' : '';
63 | return lpad(prefix+Math.abs(value).toFixed(precision),' ',width);
64 | case 'g':
65 | prefix = value < 0 ? '-' : '';
66 | return lpad(prefix+Math.abs(value).toPrecision(precision),' ',width);
67 | default:
68 | return match;
69 | }
70 |
71 | }
72 | return fmt.replace(/%(\d+)?(\.(\d+))?(l?)([%scdfFeg])/g,format);
73 | }
74 |
--------------------------------------------------------------------------------
/js/Color.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 | *
17 | **/
18 |
19 | "use strict";
20 |
21 | /**
22 | * ColorError
23 | * @constructor
24 | */
25 | var ColorError = function (message)
26 | {
27 | this.name = "ColorError";
28 | this.message = (message) ? message : "Error";
29 | };
30 | ColorError.prototype = new Error();
31 |
32 | /**
33 | * Color
34 | * @constructor
35 | */
36 | function Color(str)
37 | {
38 | var bits;
39 |
40 | this.r = 0;
41 | this.g = 0;
42 | this.b = 0;
43 | this.a = 1.0;
44 |
45 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) {
46 | this.r = parseInt(bits[1]+bits[1], 16);
47 | this.g = parseInt(bits[2]+bits[2], 16);
48 | this.b = parseInt(bits[3]+bits[3], 16);
49 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) {
50 | this.r = parseInt(bits[1], 16);
51 | this.g = parseInt(bits[2], 16);
52 | this.b = parseInt(bits[3], 16);
53 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) {
54 | this.r = parseInt(bits[1], 16);
55 | this.g = parseInt(bits[2], 16);
56 | this.b = parseInt(bits[3], 16);
57 | this.a = parseInt(bits[4], 16)/255;
58 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) {
59 | this.r = parseInt(bits[1], 10);
60 | this.g = parseInt(bits[2], 10);
61 | this.b = parseInt(bits[3], 10);
62 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) {
63 | this.r = parseInt(bits[1], 10);
64 | this.g = parseInt(bits[2], 10);
65 | this.b = parseInt(bits[3], 10);
66 | this.a = parseFloat(bits[4], 10);
67 | } else {
68 | throw new ColorError("Unknow color format '"+str+"'");
69 | }
70 | };
71 |
72 | Color.prototype.torgba = function ()
73 | {
74 | return 'rgba('+this.r+','+this.g+','+this.b+','+this.a+')';
75 | };
76 |
77 |
--------------------------------------------------------------------------------
/test/test5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RRD Canvas
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
59 |
60 |
61 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/js/base64.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | *
4 | * Base64 encode / decode
5 | * http://www.webtoolkit.info/
6 | *
7 | **/
8 |
9 | var Base64 = {
10 |
11 | // private property
12 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
13 |
14 | // public method for encoding
15 | encode : function (input) {
16 | var output = "";
17 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
18 | var i = 0;
19 |
20 | input = Base64._utf8_encode(input);
21 |
22 | while (i < input.length) {
23 |
24 | chr1 = input.charCodeAt(i++);
25 | chr2 = input.charCodeAt(i++);
26 | chr3 = input.charCodeAt(i++);
27 |
28 | enc1 = chr1 >> 2;
29 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
30 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
31 | enc4 = chr3 & 63;
32 |
33 | if (isNaN(chr2)) {
34 | enc3 = enc4 = 64;
35 | } else if (isNaN(chr3)) {
36 | enc4 = 64;
37 | }
38 |
39 | output = output +
40 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
41 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
42 |
43 | }
44 |
45 | return output;
46 | },
47 |
48 | // public method for decoding
49 | decode : function (input) {
50 | var output = "";
51 | var chr1, chr2, chr3;
52 | var enc1, enc2, enc3, enc4;
53 | var i = 0;
54 |
55 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
56 |
57 | while (i < input.length) {
58 |
59 | enc1 = this._keyStr.indexOf(input.charAt(i++));
60 | enc2 = this._keyStr.indexOf(input.charAt(i++));
61 | enc3 = this._keyStr.indexOf(input.charAt(i++));
62 | enc4 = this._keyStr.indexOf(input.charAt(i++));
63 |
64 | chr1 = (enc1 << 2) | (enc2 >> 4);
65 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
66 | chr3 = ((enc3 & 3) << 6) | enc4;
67 |
68 | output = output + String.fromCharCode(chr1);
69 |
70 | if (enc3 != 64) {
71 | output = output + String.fromCharCode(chr2);
72 | }
73 | if (enc4 != 64) {
74 | output = output + String.fromCharCode(chr3);
75 | }
76 |
77 | }
78 |
79 | output = Base64._utf8_decode(output);
80 |
81 | return output;
82 |
83 | },
84 |
85 | // private method for UTF-8 encoding
86 | _utf8_encode : function (string) {
87 | string = string.replace(/\r\n/g,"\n");
88 | var utftext = "";
89 |
90 | for (var n = 0; n < string.length; n++) {
91 |
92 | var c = string.charCodeAt(n);
93 |
94 | if (c < 128) {
95 | utftext += String.fromCharCode(c);
96 | }
97 | else if((c > 127) && (c < 2048)) {
98 | utftext += String.fromCharCode((c >> 6) | 192);
99 | utftext += String.fromCharCode((c & 63) | 128);
100 | }
101 | else {
102 | utftext += String.fromCharCode((c >> 12) | 224);
103 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
104 | utftext += String.fromCharCode((c & 63) | 128);
105 | }
106 |
107 | }
108 |
109 | return utftext;
110 | },
111 |
112 | // private method for UTF-8 decoding
113 | _utf8_decode : function (utftext) {
114 | var string = "";
115 | var i = 0;
116 | var c, c1, c2, c3;
117 | c = c1 = c2 = 0;
118 |
119 | while ( i < utftext.length ) {
120 |
121 | c = utftext.charCodeAt(i);
122 |
123 | if (c < 128) {
124 | string += String.fromCharCode(c);
125 | i++;
126 | }
127 | else if((c > 191) && (c < 224)) {
128 | c2 = utftext.charCodeAt(i+1);
129 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
130 | i += 2;
131 | }
132 | else {
133 | c2 = utftext.charCodeAt(i+1);
134 | c3 = utftext.charCodeAt(i+2);
135 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
136 | i += 3;
137 | }
138 |
139 | }
140 |
141 | return string;
142 | }
143 |
144 | };
145 |
--------------------------------------------------------------------------------
/js/strftime.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | **/
18 |
19 | "use strict";
20 |
21 | function strftime (fmt, time)
22 | {
23 | var d = new Date(time*1000);
24 |
25 | var days = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
26 | var fdays = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
27 | var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
28 | var fmonths = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
29 |
30 | function pad2 (number)
31 | {
32 | return (number < 10 ? '0' : '') + number;
33 | }
34 |
35 | function pad3(number)
36 | {
37 | return (number < 10 ? '00' : number < 100 ? '0' : '') + number;
38 | }
39 |
40 | function format(match, opt)
41 | {
42 | if (match === '%%') return '%';
43 |
44 | switch (opt) {
45 | case 'a':
46 | return days[d.getDay()];
47 | case 'A':
48 | return fdays[d.getDay()];
49 | case 'b':
50 | return months[d.getMonth()];
51 | case 'B':
52 | return fmonths[d.getMonth()];
53 | case 'c':
54 | return d.toLocaleString();
55 | case 'd':
56 | return pad2(d.getDate());
57 | case 'H':
58 | return pad2(d.getHours());
59 | case 'I':
60 | var hours = d.getHours()%12;
61 | return pad2(hours === 0 ? 12 : hours);
62 | case 'j':
63 | var d01 = new Date (d.getFullYear(), 0, 1);
64 | return pad3(Math.ceil((d.getTime()-d01.getTime())/86400000)+1);
65 | case 'm':
66 | return pad2(d.getMonth());
67 | case 'M':
68 | return pad2(d.getMinutes());
69 | case 'p':
70 | return d.getHours() >= 12 ? 'PM' : 'AM';
71 | case 's':
72 | return pad2(d.getSeconds());
73 | case 'S':
74 | return d.getTime()/1000;
75 | case 'u':
76 | return d.getDay() === 0 ? 7 : d.getDay();
77 | case 'U':
78 | var d01 = new Date(d.getFullYear(),0,1);
79 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 6 - d.getDay())/7));
80 | case 'V':
81 | var d01 = new Date(d.getFullYear(), 0, 1);
82 | var w = Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7);
83 | var d31 = new Date(d.getFullYear(), 11, 31);
84 | if (d01.getDay() < 4 && d01.getDay() > 1) w++;
85 | if (w === 53 && d31.getDay() < 4) {
86 | w = 1;
87 | } else if (w === 0) {
88 | d31 = new Date(d.getFullYear()-1, 11, 31);
89 | d01 = new Date(d31.getFullYear(), 0, 1);
90 | w = Math.round((Math.ceil((d31.getTime()-d01.getTime())/86400000)+1 + 7 - (d31.getDay() === 0 ? 7 : d31.getDay()))/7);
91 | if (d01.getDay() < 4 && d01.getDay() > 1) w++;
92 | if (w === 53 && d31.getDay() < 4) w = 1;
93 | }
94 | return pad2(w);
95 | case 'w':
96 | return d.getDay();
97 | case 'W':
98 | var d01 = new Date(d.getFullYear(),0,1);
99 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7));
100 | case 'x':
101 | return pad2(d.getDate())+'/'+pad2(d.getMonth())+'/'+d.getFullYear();
102 | case 'X':
103 | return pad2(d.getHours())+':'+pad2(d.getMinutes())+':'+pad2(d.getSeconds());
104 | case 'y':
105 | return pad2(d.getFullYear()%100);
106 | case 'Y':
107 | return d.getFullYear();
108 | case 'Z':
109 | return d.toString().replace(/^.*\(([^)]+)\)$/, '$1');
110 | default:
111 | return match;
112 | }
113 | }
114 | return fmt.replace(/%([aAbBcdHIjmMpsSUVwWxXyYZ%])/g, format);
115 | }
116 |
--------------------------------------------------------------------------------
/js/RrdDataFile.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | *
18 | * Manuel Sanmartin
19 | **/
20 |
21 | "use strict";
22 |
23 | /**
24 | * RrdDataFile
25 | * @constructor
26 | */
27 | var RrdDataFile = function() {
28 | this.init.apply(this, arguments);
29 | };
30 |
31 | RrdDataFile.prototype = {
32 | rrdfiles: null,
33 |
34 | init: function()
35 | {
36 | this.rrdfiles = {};
37 | this.rrdfiles_fetching = {};
38 | this.rrdfiles_wait = {};
39 | },
40 | build: function(gdp, ft_step, rrd)
41 | {
42 | var cal_start, cal_end;
43 | var best_full_rra = -1, best_part_rra = -1, chosen_rra = 0;
44 | var best_full_step_diff = Infinity, best_part_step_diff = Infinity;
45 | var tmp_step_diff = 0, tmp_match = 0, best_match = -1;
46 | var full_match;
47 | var data_ptr;
48 | var rows;
49 | var rra;
50 | var i, ii;
51 | var last_update = rrd.getLastUpdate();
52 |
53 | var cf_idx = gdp.cf;
54 | var ds_cnt = rrd.getNrDSs();
55 | var rra_cnt = rrd.getNrRRAs();
56 |
57 | for (i = 0; i < ds_cnt; i++)
58 | gdp.ds_namv[i] = rrd.rrd_header.getDSbyIdx(i).getName();
59 |
60 | /* if the requested graph starts after the last available data point,
61 | * return one big NaN instead of taking the finest (step=1) RRA */
62 | if (gdp.start > last_update) {
63 | ft_step = gdp.end - gdp.start;
64 | gdp.ds_cnt = ds_cnt;
65 | gdp.data = [];
66 | for (ii = 0; ii < ds_cnt; ii++)
67 | gdp.data[ii] = Number.NaN;
68 | return ft_step;
69 | }
70 |
71 | for (i = 0; i < rra_cnt; i++) {
72 | rra = rrd.getRRAInfo(i);
73 | if (RrdGraphDesc.cf_conv(rra.getCFName()) === cf_idx) {
74 | /* covered seconds in this RRA */
75 | var range_secs = rra.getStep();
76 | cal_end = last_update - (last_update % range_secs);
77 | cal_start = cal_end - (range_secs * rra.row_cnt);
78 | full_match = gdp.end - gdp.start;
79 |
80 | tmp_step_diff = Math.abs(ft_step - range_secs);
81 | if (cal_start <= gdp.start) {
82 | if (tmp_step_diff < best_full_step_diff) {
83 | best_full_step_diff = tmp_step_diff;
84 | best_full_rra = i;
85 | }
86 | } else {
87 | tmp_match = full_match;
88 | if (cal_start > gdp.start) tmp_match -= (cal_start - gdp.start);
89 | if (best_match < tmp_match || (best_match === tmp_match &&
90 | tmp_step_diff < best_part_step_diff)) {
91 | best_match = tmp_match;
92 | best_part_step_diff = tmp_step_diff;
93 | best_part_rra = i;
94 | }
95 | }
96 | }
97 | }
98 |
99 | if (best_full_rra >= 0) chosen_rra = best_full_rra;
100 | else if (best_part_rra >= 0) chosen_rra = best_part_rra;
101 | else throw "the RRD does not contain an RRA matching the chosen CF";
102 |
103 | var rra_info = rrd.getRRAInfo(chosen_rra);
104 | rra = rrd.getRRA(chosen_rra);
105 |
106 | ft_step = rra_info.getStep();
107 | gdp.start -= (gdp.start % ft_step);
108 | gdp.end += (ft_step - gdp.end % ft_step);
109 | rows = (gdp.end - gdp.start) / ft_step + 1;
110 |
111 | gdp.ds_cnt = ds_cnt;
112 | data_ptr = 0;
113 |
114 | var rra_end_time = (last_update - (last_update % ft_step));
115 | var rra_start_time = (rra_end_time - (ft_step * (rra_info.row_cnt - 1)));
116 | /* here's an error by one if we don't be careful */
117 | var start_offset = (gdp.start + ft_step - rra_start_time) / ft_step;
118 | var end_offset = (rra_end_time - gdp.end) / ft_step;
119 |
120 | gdp.data = [];
121 |
122 | for (i = start_offset; i < rra.row_cnt - end_offset; i++) {
123 | if (i < 0) {
124 | for (ii = 0; ii < ds_cnt; ii++)
125 | gdp.data[data_ptr++] = Number.NaN;
126 | } else if (i >= rra.row_cnt) {
127 | for (ii = 0; ii < ds_cnt; ii++)
128 | gdp.data[data_ptr++] = Number.NaN;
129 | } else {
130 | for(ii = 0; ii < ds_cnt; ii++)
131 | gdp.data[data_ptr++] = rra.getEl(i, ii);
132 | }
133 | }
134 | return ft_step;
135 | },
136 | fetch: function(gdp, ft_step)
137 | {
138 | var rrd;
139 |
140 | if (gdp.rrd in this.rrdfiles) {
141 | rrd = this.rrdfiles[gdp.rrd];
142 | } else {
143 | var bf = FetchBinaryURL(gdp.rrd);
144 | rrd = new RRDFile(bf);
145 | this.rrdfiles[gdp.rrd] = rrd;
146 | }
147 |
148 | return this.build(gdp, ft_step, rrd);
149 | },
150 | fetch_async_callback: function(bf, args)
151 | {
152 | var rrd;
153 |
154 | rrd = new RRDFile(bf);
155 | args.this.rrdfiles[args.gdp.rrd] = rrd;
156 | args.callback(args.callback_arg, args.this.build(args.gdp, args.ft_step, rrd));
157 |
158 | for(var vname in args.this.rrdfiles_wait)
159 | {
160 | var o_args = args.this.rrdfiles_wait[vname];
161 | if (args.gdp.rrd == o_args.gdp.rrd)
162 | {
163 | delete args.this.rrdfiles_wait[vname];
164 | o_args.callback(o_args.callback_arg, args.this.build(o_args.gdp, o_args.ft_step, rrd));
165 | }
166 | }
167 | },
168 | fetch_async: function(gdp, ft_step, callback, callback_arg)
169 | {
170 | if (gdp.rrd === null) return -1;
171 |
172 | if (gdp.rrd in this.rrdfiles) {
173 | callback(callback_arg, this.build(gdp, ft_step, this.rrdfiles[gdp.rrd]));
174 | } else if (gdp.rrd in this.rrdfiles_fetching) {
175 | this.rrdfiles_wait[gdp.vname] = { this:this, gdp: gdp, ft_step: ft_step, callback: callback, callback_arg: callback_arg };
176 | if (gdp.rrd in this.rrdfiles)
177 | {
178 | delete this.rrdfiles_wait[gdp.vname];
179 | callback(callback_arg, this.build(gdp, ft_step, this.rrdfiles[gdp.rrd]));
180 | }
181 | } else {
182 | this.rrdfiles_fetching[gdp.rrd] = FetchBinaryURLAsync(gdp.rrd, this.fetch_async_callback, { this:this, gdp: gdp, ft_step: ft_step, callback: callback, callback_arg: callback_arg });
183 | }
184 | return 0;
185 | }
186 | };
187 |
--------------------------------------------------------------------------------
/test/test6.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RRD Canvas
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
144 |
145 |
146 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/test/test2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RRD Canvas
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
144 |
145 |
146 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/test/test3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RRD Canvas
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
144 |
145 |
146 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/js/RrdGfxCanvas.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | *
18 | * Manuel Sanmartin
19 | **/
20 |
21 | "use strict";
22 |
23 | /**
24 | * RrdGfxCanvas
25 | * @constructor
26 | */
27 | var RrdGfxCanvas = function(canvasId)
28 | {
29 | this.canvas = document.getElementById(canvasId);
30 | this.ctx = this.canvas.getContext('2d');
31 | this.dash = false;
32 | this.dash_offset = null;
33 | this.dash_array = null;
34 | };
35 |
36 | RrdGfxCanvas.prototype.size = function (width, height)
37 | {
38 | this.canvas.width = width;
39 | this.canvas.height = height;
40 | };
41 |
42 | RrdGfxCanvas.prototype.set_dash = function (dashes, n, offset)
43 | {
44 | this.dash = true;
45 | this.dash_array = dashes;
46 | this.dash_offset = offset;
47 | };
48 |
49 | RrdGfxCanvas.prototype._set_dash = function ()
50 | {
51 | if (this.dash_array != undefined && this.dash_array.length > 0) {
52 | this.ctx.setLineDash(this.dash_array);
53 | if (this.dash_offset > 0) {
54 | this.ctx.lineDashOffset = this.dash_offset;
55 | }
56 | }
57 | this.dash = false;
58 | this.dash_array = null;
59 | this.dash_offset = 0;
60 | };
61 |
62 | RrdGfxCanvas.prototype.line = function (X0, Y0, X1, Y1, width, color)
63 | {
64 | X0 = Math.round(X0);
65 | Y0 = Math.round(Y0);
66 | X1 = Math.round(X1);
67 | Y1 = Math.round(Y1);
68 |
69 | if (Y0 === Y1) {
70 | Y0 += 0.5;
71 | Y1 += 0.5;
72 | } else if (X0 === X1) {
73 | X0 += 0.5;
74 | X1 += 0.5;
75 | }
76 | this.ctx.save();
77 | this.ctx.lineWidth = width;
78 | this.ctx.strokeStyle = color;
79 | if (this.dash) this._set_dash();
80 | this.ctx.beginPath();
81 | this.ctx.moveTo(X0, Y0);
82 | this.ctx.lineTo(X1, Y1);
83 | this.ctx.stroke();
84 | this.ctx.restore();
85 | };
86 |
87 | RrdGfxCanvas.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off)
88 | {
89 | var swap, n;
90 | X0 = Math.round(X0);
91 | Y0 = Math.round(Y0);
92 | X1 = Math.round(X1);
93 | Y1 = Math.round(Y1);
94 |
95 | this.ctx.save();
96 | this.ctx.lineWidth = width;
97 | this.ctx.strokeStyle = color;
98 | this.ctx.setLineDash([ dash_on, dash_off ]);
99 | this.ctx.lineDashOffset = dash_on;
100 | this.ctx.beginPath();
101 |
102 | if (Y0 === Y1) {
103 | Y0 += 0.5;
104 | Y1 += 0.5;
105 | } else if (X0 === X1) {
106 | X0 += 0.5;
107 | X1 += 0.5;
108 | }
109 |
110 | this.ctx.moveTo(X0, Y0);
111 | this.ctx.lineTo(X1, Y1);
112 | /*
113 | if (Y0 === Y1) {
114 | Y0 += 0.5;
115 | Y1 += 0.5;
116 | if (X0 > X1) {
117 | swap = X0;
118 | X0 = X1;
119 | X1 = swap;
120 | }
121 | this.ctx.moveTo(X0, Y0);
122 | n = 0;
123 | while(X0<=X1) {
124 | if (n%2 === 1) {
125 | X0 += dash_on;
126 | this.ctx.lineTo(X0, Y0);
127 | } else {
128 | X0 += dash_off;
129 | this.ctx.moveTo(X0, Y0);
130 | }
131 | n++;
132 | }
133 | } else if (X0 === X1) {
134 | X0 += 0.5;
135 | X1 += 0.5;
136 | if (Y0 > Y1) {
137 | swap = Y0;
138 | Y0 = Y1;
139 | Y1 = swap;
140 | }
141 | this.ctx.moveTo(X0, Y0);
142 | n = 0;
143 | while(Y0<=Y1) {
144 | if (n%2 === 1) {
145 | Y0 += dash_on;
146 | this.ctx.lineTo(X0, Y0);
147 | } else {
148 | Y0 += dash_off;
149 | this.ctx.moveTo(X0, Y0);
150 | }
151 | n++;
152 | }
153 |
154 | } else {
155 | this.ctx.moveTo(X0, Y0);
156 | this.ctx.lineTo(X1, Y1);
157 | }
158 | */
159 | this.ctx.stroke();
160 | this.ctx.restore();
161 | };
162 |
163 | RrdGfxCanvas.prototype.rectangle = function (X0, Y0, X1, Y1, width, style)
164 | {
165 | X0 = Math.round(X0)+0.5;
166 | X1 = Math.round(X1)+0.5;
167 | Y0 = Math.round(Y0)+0.5;
168 | Y1 = Math.round(Y1)+0.5;
169 |
170 | this.ctx.save();
171 | this.ctx.beginPath();
172 | if (this.dash) this._set_dash();
173 | this.ctx.lineWidth = width;
174 | this.ctx.moveTo(X0, Y0);
175 | this.ctx.lineTo(X1, Y0);
176 | this.ctx.lineTo(X1, Y1);
177 | this.ctx.lineTo(X0, Y1);
178 | this.ctx.closePath();
179 | this.ctx.strokeStyle = style;
180 | this.ctx.stroke();
181 | this.ctx.restore();
182 | };
183 |
184 | RrdGfxCanvas.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color)
185 | {
186 | X0 = Math.round(X0)+0.5;
187 | Y0 = Math.round(Y0)+0.5;
188 | X1 = Math.round(X1)+0.5;
189 | Y1 = Math.round(Y1)+0.5;
190 | X2 = Math.round(X2)+0.5;
191 | Y2 = Math.round(Y2)+0.5;
192 | this.ctx.fillStyle = color;
193 | this.ctx.beginPath();
194 | this.ctx.moveTo(X0, Y0);
195 | this.ctx.lineTo(X1, Y1);
196 | this.ctx.lineTo(X2, Y2);
197 | };
198 |
199 | RrdGfxCanvas.prototype.add_point = function (x, y)
200 | {
201 | x = Math.round(x)+0.5;
202 | y = Math.round(y)+0.5;
203 | this.ctx.lineTo(x, y);
204 | };
205 |
206 | RrdGfxCanvas.prototype.close_path = function ()
207 | {
208 | this.ctx.closePath();
209 | this.ctx.fill();
210 | };
211 |
212 | RrdGfxCanvas.prototype.stroke_begin = function (width, style)
213 | {
214 | this.ctx.save();
215 | this.ctx.beginPath();
216 | if (this.dash) this._set_dash();
217 | this.ctx.lineWidth = width;
218 | this.ctx.strokeStyle = style;
219 | this.ctx.lineCap = 'round';
220 | this.ctx.round = 'round';
221 | };
222 |
223 | RrdGfxCanvas.prototype.stroke_end = function ()
224 | {
225 | this.ctx.stroke();
226 | this.ctx.restore();
227 | };
228 |
229 | RrdGfxCanvas.prototype.moveTo = function (x,y)
230 | {
231 | x = Math.round(x)+0.5;
232 | y = Math.round(y)+0.5;
233 | this.ctx.moveTo(x, y);
234 | };
235 |
236 | RrdGfxCanvas.prototype.lineTo = function (x,y)
237 | {
238 | x = Math.round(x)+0.5;
239 | y = Math.round(y)+0.5;
240 | this.ctx.lineTo(x, y);
241 | };
242 |
243 | RrdGfxCanvas.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text)
244 | {
245 | x = Math.round(x);
246 | y = Math.round(y);
247 |
248 | this.ctx.save();
249 | this.ctx.font = font.size+'px '+font.font;
250 |
251 | switch (h_align) {
252 | case RrdGraph.GFX_H_LEFT:
253 | this.ctx.textAlign = 'left';
254 | break;
255 | case RrdGraph.GFX_H_RIGHT:
256 | this.ctx.textAlign = 'right';
257 | break;
258 | case RrdGraph.GFX_H_CENTER:
259 | this.ctx.textAlign = 'center';
260 | break;
261 | }
262 |
263 | switch (v_align) {
264 | case RrdGraph.GFX_V_TOP:
265 | this.ctx.textBaseline = 'top';
266 | break;
267 | case RrdGraph.GFX_V_BOTTOM:
268 | this.ctx.textBaseline = 'bottom';
269 | break;
270 | case RrdGraph.GFX_V_CENTER:
271 | this.ctx.textBaseline = 'middle';
272 | break;
273 | }
274 |
275 | this.ctx.fillStyle = color;
276 | this.ctx.translate(x,y);
277 | this.ctx.rotate(-angle*Math.PI/180.0);
278 | this.ctx.fillText(text, 0, 0);
279 | this.ctx.restore();
280 | };
281 |
282 | RrdGfxCanvas.prototype.get_text_width = function(start, font, tabwidth, text)
283 | {
284 | this.ctx.save();
285 | this.ctx.font = font.size+'px '+font.font;
286 | var width = this.ctx.measureText(text);
287 | this.ctx.restore();
288 | return width.width;
289 | };
290 |
291 |
--------------------------------------------------------------------------------
/js/RrdGfxSvg.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | *
18 | * Manuel Sanmartin
19 | **/
20 |
21 | "use strict";
22 |
23 | /**
24 | * RrdGfxSvg
25 | * @constructor
26 | */
27 | var RrdGfxSvg = function(svgId)
28 | {
29 | this.svg = document.getElementById(svgId);
30 | this.svgns = "http://www.w3.org/2000/svg";
31 | this.xmlns = "http://www.w3.org/XML/1998/namespace";
32 | this.path = null;
33 | this.path_color = null;
34 | this.path_width = null;
35 | this.dash = false;
36 | this.dash_offset = null;
37 | this.dash_array = null;
38 | };
39 |
40 | RrdGfxSvg.prototype.size = function (width, height)
41 | {
42 | while(this.svg.lastChild)
43 | this.svg.removeChild(this.svg.lastChild);
44 |
45 | this.svg.setAttribute("width", width+"px");
46 | this.svg.setAttribute("height", height+"px");
47 | this.svg.setAttribute("viewBox", "0 0 "+width+" "+height);
48 | };
49 |
50 | RrdGfxSvg.prototype.set_dash = function (dashes, n, offset)
51 | {
52 | this.dash = true;
53 | this.dash_array = dashes;
54 | this.dash_offset = offset;
55 | };
56 |
57 | RrdGfxSvg.prototype._set_dash = function (shape)
58 | {
59 | if (this.dash_array != undefined && this.dash_array.length > 0) {
60 | shape.setAttributeNS(null, "stroke-dasharray", this.dash_array.join(','));
61 | if (this.dash_offset > 0) {
62 | shape.setAttributeNS(null, "stroke-dashoffset", this.dash_offset);
63 | }
64 | }
65 | this.dash = false;
66 | this.dash_array = null;
67 | this.dash_offset = 0;
68 | }
69 |
70 | RrdGfxSvg.prototype.line = function (X0, Y0, X1, Y1, width, color)
71 | {
72 | var shape = document.createElementNS(this.svgns, "line");
73 |
74 | X0 = Math.round(X0)+0.5;
75 | Y0 = Math.round(Y0)+0.5;
76 | X1 = Math.round(X1)+0.5;
77 | Y1 = Math.round(Y1)+0.5;
78 |
79 | shape.setAttributeNS(null, "x1", X0);
80 | shape.setAttributeNS(null, "y1", Y0);
81 | shape.setAttributeNS(null, "x2", X1);
82 | shape.setAttributeNS(null, "y2", Y1);
83 | shape.setAttributeNS(null, "stroke-width", width);
84 | shape.setAttributeNS(null, "stroke", color);
85 | if (this.dash)
86 | this._set_dash(shape);
87 |
88 | this.svg.appendChild(shape);
89 | };
90 |
91 | RrdGfxSvg.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off)
92 | {
93 | var shape = document.createElementNS(this.svgns, "line");
94 |
95 | X0 = Math.round(X0)+0.5;
96 | Y0 = Math.round(Y0)+0.5;
97 | X1 = Math.round(X1)+0.5;
98 | Y1 = Math.round(Y1)+0.5;
99 |
100 | shape.setAttributeNS(null, "x1", X0);
101 | shape.setAttributeNS(null, "y1", Y0);
102 | shape.setAttributeNS(null, "x2", X1);
103 | shape.setAttributeNS(null, "y2", Y1);
104 | shape.setAttributeNS(null, "stroke-width", width);
105 | shape.setAttributeNS(null, "stroke", color);
106 | shape.setAttributeNS(null, "stroke-dasharray", dash_on+','+dash_off);
107 |
108 | this.svg.appendChild(shape);
109 | };
110 |
111 | RrdGfxSvg.prototype.rectangle = function (X0, Y0, X1, Y1, width, style)
112 | {
113 | var shape = document.createElementNS(this.svgns, "rect");
114 |
115 | var rwidth = Math.abs(X1-X0);
116 | var rheight = Math.abs(Y1-Y0);
117 |
118 | shape.setAttributeNS(null, "x", Math.round(X0)+0.5);
119 | shape.setAttributeNS(null, "y", Math.round(Y0-rheight)+0.5);
120 | shape.setAttributeNS(null, "width", rwidth);
121 | shape.setAttributeNS(null, "height", rheight);
122 | shape.setAttributeNS(null, "stroke-width", width);
123 | shape.setAttributeNS(null, "stroke", style);
124 | shape.setAttributeNS(null, "fill", "none");
125 | if (this.dash)
126 | this._set_dash(shape);
127 |
128 | this.svg.appendChild(shape);
129 | };
130 |
131 | RrdGfxSvg.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color)
132 | {
133 | X0 = Math.round(X0)+0.5;
134 | Y0 = Math.round(Y0)+0.5;
135 | X1 = Math.round(X1)+0.5;
136 | Y1 = Math.round(Y1)+0.5;
137 | X2 = Math.round(X2)+0.5;
138 | Y2 = Math.round(Y2)+0.5;
139 |
140 | this.path_color = color;
141 | this.path = 'M'+X0+','+Y0;
142 | this.path += ' L'+X1+','+Y1;
143 | this.path += ' L'+X2+','+Y2;
144 | };
145 |
146 | RrdGfxSvg.prototype.add_point = function (x, y)
147 | {
148 | x = Math.round(x)+0.5;
149 | y = Math.round(y)+0.5;
150 |
151 | this.path += ' L'+x+','+y;
152 | };
153 |
154 | RrdGfxSvg.prototype.close_path = function ()
155 | {
156 | var shape = document.createElementNS(this.svgns, "path");
157 |
158 | this.path += ' Z';
159 |
160 | shape.setAttributeNS(null, "d", this.path);
161 | shape.setAttributeNS(null, "fill", this.path_color);
162 | shape.setAttributeNS(null, "stroke", 'none');
163 |
164 | this.svg.appendChild(shape);
165 | };
166 |
167 | RrdGfxSvg.prototype.stroke_begin = function (width, style)
168 | {
169 | this.path_width = width;
170 | this.path_color = style;
171 | this.path = '';
172 | };
173 |
174 | RrdGfxSvg.prototype.stroke_end = function ()
175 | {
176 | var shape = document.createElementNS(this.svgns, "path");
177 |
178 | shape.setAttributeNS(null, "d", this.path);
179 | shape.setAttributeNS(null, "fill", 'none');
180 | shape.setAttributeNS(null, "stroke", this.path_color);
181 | shape.setAttributeNS(null, "stroke-width", this.path_width);
182 | shape.setAttributeNS(null, "stroke-linecap", 'round');
183 | shape.setAttributeNS(null, "stroke-linejoin", 'round');
184 | if (this.dash)
185 | this._set_dash(shape);
186 |
187 | this.svg.appendChild(shape);
188 | };
189 |
190 | RrdGfxSvg.prototype.moveTo = function (x,y)
191 | {
192 | x = Math.round(x)+0.5;
193 | y = Math.round(y)+0.5;
194 |
195 | this.path += ' M'+x+','+y;
196 | };
197 |
198 | RrdGfxSvg.prototype.lineTo = function (x,y)
199 | {
200 | x = Math.round(x)+0.5;
201 | y = Math.round(y)+0.5;
202 |
203 | this.path += ' L'+x+','+y;
204 | };
205 |
206 | RrdGfxSvg.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text)
207 | {
208 | x = Math.round(x);
209 | y = Math.round(y);
210 |
211 | var svgtext = document.createElementNS(this.svgns, "text");
212 |
213 | var data = document.createTextNode(text);
214 |
215 | svgtext.setAttributeNS(null, "x", x);
216 | svgtext.setAttributeNS(null, "y", y);
217 | svgtext.setAttributeNS(null, "fill", color);
218 | svgtext.setAttributeNS(null, "stroke", "none");
219 | svgtext.setAttributeNS(null, "font-family", font.font);
220 | svgtext.setAttributeNS(null, "font-size", font.size+"px");
221 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve");
222 |
223 | angle=-angle;
224 | svgtext.setAttributeNS(null, "transform", 'rotate('+angle+' '+x+','+y+')' );
225 |
226 | switch (h_align) {
227 | case RrdGraph.GFX_H_LEFT:
228 | svgtext.setAttributeNS(null, "text-anchor", 'start');
229 | break;
230 | case RrdGraph.GFX_H_RIGHT:
231 | svgtext.setAttributeNS(null, "text-anchor", 'end');
232 | break;
233 | case RrdGraph.GFX_H_CENTER:
234 | svgtext.setAttributeNS(null, "text-anchor", 'middle');
235 | break;
236 | }
237 | svgtext.appendChild(data);
238 | this.svg.appendChild(svgtext);
239 |
240 | var bbox = svgtext.getBBox();
241 |
242 | switch (v_align) { // FIXME
243 | case RrdGraph.GFX_V_TOP:
244 | svgtext.setAttributeNS(null, "y", y+bbox.height/2);
245 | break;
246 | case RrdGraph.GFX_V_BOTTOM:
247 | svgtext.setAttributeNS(null, "y", y-bbox.height/6);
248 | break;
249 | case RrdGraph.GFX_V_CENTER:
250 | svgtext.setAttributeNS(null, "y", y+bbox.height/4);
251 | break;
252 | }
253 | };
254 |
255 | RrdGfxSvg.prototype.get_text_width = function(start, font, tabwidth, text)
256 | {
257 | var svgtext = document.createElementNS(this.svgns, "text");
258 | var data = document.createTextNode(text);
259 | svgtext.setAttributeNS(null, "x", 0);
260 | svgtext.setAttributeNS(null, "y", 0);
261 | svgtext.setAttributeNS(null, "fill", 'none');
262 | svgtext.setAttributeNS(null, "stroke", 'none');
263 | svgtext.setAttributeNS(null, "font-family", font.font);
264 | svgtext.setAttributeNS(null, "font-size", font.size+"px");
265 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve");
266 | svgtext.appendChild(data);
267 | this.svg.appendChild(svgtext);
268 |
269 | var bbox = svgtext.getBBox();
270 |
271 | svgtext.removeChild(data);
272 | this.svg.removeChild(svgtext);
273 |
274 | return bbox.width;
275 | };
276 |
277 |
--------------------------------------------------------------------------------
/js/binaryXHR.js:
--------------------------------------------------------------------------------
1 | // jshint browser:true
2 | /*
3 | * BinaryFile over XMLHttpRequest
4 | * Part of the javascriptRRD package
5 | * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu
6 | * MIT License [http://www.opensource.org/licenses/mit-license.php]
7 | *
8 | * Original repository: http://javascriptrrd.sourceforge.net/
9 | *
10 | * Based on:
11 | * Binary Ajax 0.1.5
12 | * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
13 | * MIT License [http://www.opensource.org/licenses/mit-license.php]
14 | */
15 |
16 | // ============================================================
17 | // Exception class
18 | function InvalidBinaryFile(msg) {
19 | "use strict";
20 | this.message = msg;
21 | this.name = "Invalid BinaryFile";
22 | }
23 |
24 | // pretty print
25 | InvalidBinaryFile.prototype.toString = function() {
26 | "use strict";
27 | return this.name + ': "' + this.message + '"';
28 | };
29 |
30 | // =====================================================================
31 | // BinaryFile class
32 | // Allows access to element inside a binary stream
33 | function BinaryFile(data) {
34 | "use strict";
35 | var dataLength;
36 | // whether the data is in little endian format
37 | var littleEndian = true;
38 |
39 | this.getRawData = function() {
40 | return data;
41 | };
42 |
43 | if (typeof data === "string") {
44 | dataLength = data.length;
45 |
46 | this.getByteAt = function(iOffset) {
47 | return data.charCodeAt(iOffset) & 0xFF;
48 | };
49 | } else if (typeof DataView != "undefined" && data instanceof ArrayBuffer) {
50 | dataLength = data.dataLength;
51 | /*@cc_on
52 | } else if (typeof data === "unknown") {
53 | // Correct. "unknown" as type. MS JScript 8 added this.
54 | dataLength = IEBinary_getLength(data);
55 |
56 | this.getByteAt = function(iOffset) {
57 | return IEBinary_getByteAt(data, iOffset);
58 | };
59 | @*/
60 | } else {
61 | throw new InvalidBinaryFile("Unsupported type " + (typeof data));
62 | }
63 |
64 | this.getLength = function() {
65 | return dataLength;
66 | };
67 |
68 | if (typeof DataView != "undefined" && data instanceof ArrayBuffer) {
69 | // not an antique browser, use faster TypedArrays
70 | this.extendWithDataView(data, littleEndian);
71 | // other functions here do not need these
72 | data = null;
73 | } else {
74 | // antique browser, use slower fallback implementation
75 | this.extendWithFallback(data, littleEndian);
76 | }
77 | }
78 |
79 | BinaryFile.prototype.extendWithFallback = function(data, littleEndian) {
80 | "use strict";
81 | var doubleMantExpHi = Math.pow(2,-28);
82 | var doubleMantExpLo = Math.pow(2,-52);
83 | var doubleMantExpFast = Math.pow(2,-20);
84 |
85 | // private function for getting bytes depending on endianess
86 | var that = this, getEndianByteAt;
87 | if (littleEndian) {
88 | getEndianByteAt = function(iOffset, width, delta) {
89 | return that.getByteAt(iOffset + delta);
90 | };
91 | } else {
92 | getEndianByteAt = function(iOffset, width, delta) {
93 | return that.getByteAt(iOffset + width - delta - 1);
94 | };
95 | }
96 |
97 | this.getSByteAt = function(iOffset) {
98 | var iByte = this.getByteAt(iOffset);
99 | if (iByte > 127)
100 | return iByte - 256;
101 | else
102 | return iByte;
103 | };
104 | this.getShortAt = function(iOffset) {
105 | var iShort = (getEndianByteAt(iOffset,2,1) << 8) + getEndianByteAt(iOffset,2,0);
106 | if (iShort < 0) iShort += 65536;
107 | return iShort;
108 | };
109 | this.getSShortAt = function(iOffset) {
110 | var iUShort = this.getShortAt(iOffset);
111 | if (iUShort > 32767)
112 | return iUShort - 65536;
113 | else
114 | return iUShort;
115 | };
116 | this.getLongAt = function(iOffset) {
117 | var iByte1 = getEndianByteAt(iOffset,4,0),
118 | iByte2 = getEndianByteAt(iOffset,4,1),
119 | iByte3 = getEndianByteAt(iOffset,4,2),
120 | iByte4 = getEndianByteAt(iOffset,4,3);
121 |
122 | var iLong = (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
123 | if (iLong < 0) iLong += 4294967296;
124 | return iLong;
125 | };
126 | this.getSLongAt = function(iOffset) {
127 | var iULong = this.getLongAt(iOffset);
128 | if (iULong > 2147483647)
129 | return iULong - 4294967296;
130 | else
131 | return iULong;
132 | };
133 | this.getCharAt = function(iOffset) {
134 | return String.fromCharCode(this.getByteAt(iOffset));
135 | };
136 | this.getCStringAt = function(iOffset, iMaxLength) {
137 | var aStr = [];
138 | for (var i=iOffset,j=0;(i0);i++,j++) {
139 | aStr[j] = String.fromCharCode(this.getByteAt(i));
140 | }
141 | return aStr.join("");
142 | };
143 | this.getDoubleAt = function(iOffset) {
144 | var iByte1 = getEndianByteAt(iOffset,8,0),
145 | iByte2 = getEndianByteAt(iOffset,8,1),
146 | iByte3 = getEndianByteAt(iOffset,8,2),
147 | iByte4 = getEndianByteAt(iOffset,8,3),
148 | iByte5 = getEndianByteAt(iOffset,8,4),
149 | iByte6 = getEndianByteAt(iOffset,8,5),
150 | iByte7 = getEndianByteAt(iOffset,8,6),
151 | iByte8 = getEndianByteAt(iOffset,8,7);
152 | var iSign=iByte8 >> 7;
153 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4);
154 | var iMantHi=((((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5) << 8) + iByte4;
155 | var iMantLo=((((iByte3) << 8) + iByte2) << 8) + iByte1;
156 |
157 | if (iExpRaw === 0) return 0.0;
158 | if (iExpRaw === 0x7ff) return undefined;
159 |
160 | var iExp=(iExpRaw & 0x7FF)-1023;
161 |
162 | var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMantLo*doubleMantExpLo + iMantHi*doubleMantExpHi);
163 | return dDouble;
164 | };
165 | // Extracts only 4 bytes out of 8, loosing in precision (20 bit mantissa)
166 | this.getFastDoubleAt = function(iOffset) {
167 | var iByte5 = getEndianByteAt(iOffset,8,4),
168 | iByte6 = getEndianByteAt(iOffset,8,5),
169 | iByte7 = getEndianByteAt(iOffset,8,6),
170 | iByte8 = getEndianByteAt(iOffset,8,7);
171 | var iSign=iByte8 >> 7;
172 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4);
173 | var iMant=((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5;
174 |
175 | if (iExpRaw === 0) return 0.0;
176 | if (iExpRaw === 0x7ff) return undefined;
177 |
178 | var iExp=(iExpRaw & 0x7FF)-1023;
179 |
180 | var dDouble = ((iSign === 1) ? -1 : 1);
181 | dDouble *= Math.pow(2,iExp) * (1.0 + iMant*doubleMantExpFast);
182 | return dDouble;
183 | };
184 | };
185 |
186 | BinaryFile.prototype.extendWithDataView = function(data, littleEndian) {
187 | "use strict";
188 | var dv = new DataView(data);
189 |
190 | this.getByteAt = dv.getUint8.bind(dv);
191 | this.getSByteAt = dv.getInt8.bind(dv);
192 | this.getShortAt = function(iOffset) {
193 | return dv.getUint16(iOffset, littleEndian);
194 | };
195 | this.getSShortAt = function(iOffset) {
196 | return dv.getInt16(iOffset, littleEndian);
197 | };
198 | this.getLongAt = function(iOffset) {
199 | return dv.getUint32(iOffset, littleEndian);
200 | };
201 | this.getSLongAt = function(iOffset) {
202 | return dv.getInt32(iOffset, littleEndian);
203 | };
204 | this.getCharAt = function(iOffset) {
205 | return String.fromCharCode(this.getByteAt(iOffset));
206 | };
207 | this.getCStringAt = function(iOffset, iMaxLength) {
208 | var str = "";
209 | do {
210 | var b = this.getByteAt(iOffset++);
211 | if (b === 0)
212 | break;
213 | str += String.fromCharCode(b);
214 | } while (--iMaxLength > 0);
215 | return str;
216 | };
217 | this.getDoubleAt = function(iOffset) {
218 | return dv.getFloat64(iOffset, littleEndian);
219 | };
220 | this.getFastDoubleAt = this.getDoubleAt.bind(this);
221 | };
222 |
223 |
224 | // Use document.write only for stone-age browsers.
225 | /*@cc on
226 | document.write(
227 | "\r\n"
235 | );
236 | @*/
237 |
238 |
239 | // ===============================================================
240 | // Load a binary file from the specified URL
241 | // Will return an object of type BinaryFile
242 | function FetchBinaryURL(url) {
243 | "use strict";
244 | var request = new XMLHttpRequest();
245 | request.open("GET", url,false);
246 | try {
247 | request.overrideMimeType('text/plain; charset=x-user-defined');
248 | } catch (err) {
249 | // ignore any error, just to make both FF and IE work
250 | }
251 | request.send(null);
252 |
253 | var response = request.responseText;
254 | /*@cc_on
255 | try {
256 | // for older IE versions, the value in responseText is not usable
257 | if (IEBinary_getLength(this.responseBody)>0) {
258 | // will get here only for older verson of IE
259 | response=this.responseBody;
260 | }
261 | } catch (err) {
262 | }
263 | @*/
264 |
265 | // cannot use responseType == "arraybuffer" for synchronous requests, so
266 | // convert it afterwards
267 | if (typeof ArrayBuffer != "undefined") {
268 | var buffer = new ArrayBuffer(response.length);
269 | var bv = new Uint8Array(buffer);
270 | for (var i = 0; i < response.length; i++) {
271 | bv[i] = response.charCodeAt(i);
272 | }
273 | response = buffer;
274 | }
275 |
276 | var bf = new BinaryFile(response);
277 | return bf;
278 | }
279 |
280 |
281 | // ===============================================================
282 | // Asyncronously load a binary file from the specified URL
283 | //
284 | // callback must be a function with one or two arguments:
285 | // - bf = an object of type BinaryFile
286 | // - optional argument object (used only if callback_arg not undefined)
287 | function FetchBinaryURLAsync(url, callback, callback_arg) {
288 | "use strict";
289 | var callback_wrapper = function() {
290 | if(this.readyState === 4) {
291 | // ArrayBuffer response or just the response as string
292 | var response = this.response || this.responseText;
293 | /*@cc_on
294 | try {
295 | // for older IE versions, the value in responseText is not usable
296 | if (IEBinary_getLength(this.responseBody)>0) {
297 | // will get here only for older verson of IE
298 | response=this.responseBody;
299 | }
300 | } catch (err) {
301 | }
302 | @*/
303 |
304 | var bf = new BinaryFile(response);
305 | if (callback_arg) {
306 | callback(bf, callback_arg);
307 | } else {
308 | callback(bf);
309 | }
310 | }
311 | };
312 |
313 | var request = new XMLHttpRequest();
314 | request.onreadystatechange = callback_wrapper;
315 | request.open("GET", url, true);
316 | // Supported since Chrome 10, FF 6, IE 10, Opera 11.60 (source: MDN)
317 | request.responseType = "arraybuffer";
318 | try {
319 | request.overrideMimeType('text/plain; charset=x-user-defined');
320 | } catch (err) {
321 | // ignore any error, just to make both FF and IE work
322 | }
323 | request.send(null);
324 | return request;
325 | }
326 |
--------------------------------------------------------------------------------
/test/test1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RRD Canvas
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/js/rrdFile.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Client library for access to RRD archive files
3 | * Part of the javascriptRRD package
4 | * Copyright (c) 2009-2010 Frank Wuerthwein, fkw@ucsd.edu
5 | * Igor Sfiligoi, isfiligoi@ucsd.edu
6 | *
7 | * Original repository: http://javascriptrrd.sourceforge.net/
8 | *
9 | * MIT License [http://www.opensource.org/licenses/mit-license.php]
10 | *
11 | */
12 |
13 | /*
14 | *
15 | * RRDTool has been developed and is maintained by
16 | * Tobias Oether [http://oss.oetiker.ch/rrdtool/]
17 | *
18 | * This software can be used to read files produced by the RRDTool
19 | * but has been developed independently.
20 | *
21 | * Limitations:
22 | *
23 | * This version of the module assumes RRD files created on linux
24 | * with intel architecture and supports both 32 and 64 bit CPUs.
25 | * All integers in RRD files are suppoes to fit in 32bit values.
26 | *
27 | * Only versions 3 and 4 of the RRD archive are supported.
28 | *
29 | * Only AVERAGE,MAXIMUM,MINIMUM and LAST consolidation functions are
30 | * supported. For all others, the behaviour is at the moment undefined.
31 | *
32 | */
33 |
34 | /*
35 | * Dependencies:
36 | *
37 | * The data provided to this module require an object of a class
38 | * that implements the following methods:
39 | * getByteAt(idx) - Return a 8 bit unsigned integer at offset idx
40 | * getLongAt(idx) - Return a 32 bit unsigned integer at offset idx
41 | * getDoubleAt(idx) - Return a double float at offset idx
42 | * getFastDoubleAt(idx) - Similar to getDoubleAt but with less precision
43 | * getCStringAt(idx,maxsize) - Return a string of at most maxsize characters
44 | * that was 0-terminated in the source
45 | *
46 | * The BinaryFile from binaryXHR.js implements this interface.
47 | *
48 | */
49 |
50 |
51 | // ============================================================
52 | // Exception class
53 | function InvalidRRD(msg) {
54 | this.message=msg;
55 | this.name="Invalid RRD";
56 | }
57 |
58 | // pretty print
59 | InvalidRRD.prototype.toString = function() {
60 | return this.name + ': "' + this.message + '"';
61 | }
62 |
63 |
64 | // ============================================================
65 | // RRD DS Info class
66 | function RRDDS(rrd_data,rrd_data_idx,my_idx) {
67 | this.rrd_data=rrd_data;
68 | this.rrd_data_idx=rrd_data_idx;
69 | this.my_idx=my_idx;
70 | }
71 |
72 | RRDDS.prototype.getIdx = function() {
73 | return this.my_idx;
74 | }
75 | RRDDS.prototype.getName = function() {
76 | return this.rrd_data.getCStringAt(this.rrd_data_idx,20);
77 | }
78 | RRDDS.prototype.getType = function() {
79 | return this.rrd_data.getCStringAt(this.rrd_data_idx+20,20);
80 | }
81 | RRDDS.prototype.getMin = function() {
82 | return this.rrd_data.getDoubleAt(this.rrd_data_idx+48);
83 | }
84 | RRDDS.prototype.getMax = function() {
85 | return this.rrd_data.getDoubleAt(this.rrd_data_idx+56);
86 | }
87 |
88 |
89 | // ============================================================
90 | // RRD RRA Info class
91 | function RRDRRAInfo(rrd_data,rra_def_idx,
92 | int_align,row_cnt,pdp_step,my_idx) {
93 | this.rrd_data=rrd_data;
94 | this.rra_def_idx=rra_def_idx;
95 | this.int_align=int_align;
96 | this.row_cnt=row_cnt;
97 | this.pdp_step=pdp_step;
98 | this.my_idx=my_idx;
99 |
100 | // char nam[20], uint row_cnt, uint pdp_cnt
101 | this.rra_pdp_cnt_idx=rra_def_idx+Math.ceil(20/int_align)*int_align+int_align;
102 | }
103 |
104 | RRDRRAInfo.prototype.getIdx = function() {
105 | return this.my_idx;
106 | }
107 |
108 | // Get number of rows
109 | RRDRRAInfo.prototype.getNrRows = function() {
110 | return this.row_cnt;
111 | }
112 |
113 | // Get number of slots used for consolidation
114 | // Mostly for internal use
115 | RRDRRAInfo.prototype.getPdpPerRow = function() {
116 | return this.rrd_data.getLongAt(this.rra_pdp_cnt_idx);
117 | }
118 |
119 | // Get RRA step (expressed in seconds)
120 | RRDRRAInfo.prototype.getStep = function() {
121 | return this.pdp_step*this.getPdpPerRow();
122 | }
123 |
124 | // Get consolidation function name
125 | RRDRRAInfo.prototype.getCFName = function() {
126 | return this.rrd_data.getCStringAt(this.rra_def_idx,20);
127 | }
128 |
129 |
130 | // ============================================================
131 | // RRD RRA handling class
132 | function RRDRRA(rrd_data,rra_ptr_idx,
133 | rra_info,
134 | header_size,prev_row_cnts,ds_cnt) {
135 | this.rrd_data=rrd_data;
136 | this.rra_info=rra_info;
137 | this.row_cnt=rra_info.row_cnt;
138 | this.ds_cnt=ds_cnt;
139 |
140 | var row_size=ds_cnt*8;
141 |
142 | this.base_rrd_db_idx=header_size+prev_row_cnts*row_size;
143 |
144 | // get imediately, since it will be needed often
145 | this.cur_row=rrd_data.getLongAt(rra_ptr_idx);
146 |
147 | // calculate idx relative to base_rrd_db_idx
148 | // mostly used internally
149 | this.calc_idx = function(row_idx,ds_idx) {
150 | if ((row_idx>=0) && (row_idx=0) && (ds_idx=this.row_cnt) real_row_idx-=this.row_cnt;
155 | return row_size*real_row_idx+ds_idx*8;
156 | } else {
157 | throw RangeError("DS idx ("+ row_idx +") out of range [0-" + ds_cnt +").");
158 | }
159 | } else {
160 | throw RangeError("Row idx ("+ row_idx +") out of range [0-" + this.row_cnt +").");
161 | }
162 | }
163 | }
164 |
165 | RRDRRA.prototype.getIdx = function() {
166 | return this.rra_info.getIdx();
167 | }
168 |
169 | // Get number of rows/columns
170 | RRDRRA.prototype.getNrRows = function() {
171 | return this.row_cnt;
172 | }
173 | RRDRRA.prototype.getNrDSs = function() {
174 | return this.ds_cnt;
175 | }
176 |
177 | // Get RRA step (expressed in seconds)
178 | RRDRRA.prototype.getStep = function() {
179 | return this.rra_info.getStep();
180 | }
181 |
182 | // Get consolidation function name
183 | RRDRRA.prototype.getCFName = function() {
184 | return this.rra_info.getCFName();
185 | }
186 |
187 | RRDRRA.prototype.getEl = function(row_idx,ds_idx) {
188 | return this.rrd_data.getDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx));
189 | }
190 |
191 | // Low precision version of getEl
192 | // Uses getFastDoubleAt
193 | RRDRRA.prototype.getElFast = function(row_idx,ds_idx) {
194 | return this.rrd_data.getFastDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx));
195 | }
196 |
197 | // ============================================================
198 | // RRD Header handling class
199 | function RRDHeader(rrd_data) {
200 | this.rrd_data=rrd_data;
201 | this.validate_rrd();
202 | this.calc_idxs();
203 | }
204 |
205 | // Internal, used for initialization
206 | RRDHeader.prototype.validate_rrd = function() {
207 | if (this.rrd_data.getLength()<1) throw new InvalidRRD("Empty file.");
208 | if (this.rrd_data.getLength()<16) throw new InvalidRRD("File too short.");
209 | if (this.rrd_data.getCStringAt(0,4)!=="RRD") throw new InvalidRRD("Wrong magic id.");
210 |
211 | this.rrd_version=this.rrd_data.getCStringAt(4,5);
212 | if ((this.rrd_version!=="0003")&&(this.rrd_version!=="0004")&&(this.rrd_version!=="0001")) {
213 | throw new InvalidRRD("Unsupported RRD version "+this.rrd_version+".");
214 | }
215 |
216 | this.float_width=8;
217 | if (this.rrd_data.getLongAt(12)==0) {
218 | // not a double here... likely 64 bit
219 | this.float_align=8;
220 | if (! (this.rrd_data.getDoubleAt(16)==8.642135e+130)) {
221 | // uhm... wrong endian?
222 | this.rrd_data.switch_endian=true;
223 | }
224 | if (this.rrd_data.getDoubleAt(16)==8.642135e+130) {
225 | // now, is it all 64bit or only float 64 bit?
226 | if (this.rrd_data.getLongAt(28)==0) {
227 | // true 64 bit align
228 | this.int_align=8;
229 | this.int_width=8;
230 | } else {
231 | // integers are 32bit aligned
232 | this.int_align=4;
233 | this.int_width=4;
234 | }
235 | } else {
236 | throw new InvalidRRD("Magic float not found at 16.");
237 | }
238 | } else {
239 | /// should be 32 bit alignment
240 | if (! (this.rrd_data.getDoubleAt(12)==8.642135e+130)) {
241 | // uhm... wrong endian?
242 | this.rrd_data.switch_endian=true;
243 | }
244 | if (this.rrd_data.getDoubleAt(12)==8.642135e+130) {
245 | this.float_align=4;
246 | this.int_align=4;
247 | this.int_width=4;
248 | } else {
249 | throw new InvalidRRD("Magic float not found at 12.");
250 | }
251 | }
252 | this.unival_width=this.float_width;
253 | this.unival_align=this.float_align;
254 |
255 | // process the header here, since I need it for validation
256 |
257 | // char magic[4], char version[5], double magic_float
258 |
259 | // long ds_cnt, long rra_cnt, long pdp_step, unival par[10]
260 | this.ds_cnt_idx=Math.ceil((4+5)/this.float_align)*this.float_align+this.float_width;
261 | this.rra_cnt_idx=this.ds_cnt_idx+this.int_width;
262 | this.pdp_step_idx=this.rra_cnt_idx+this.int_width;
263 |
264 | //always get only the low 32 bits, the high 32 on 64 bit archs should always be 0
265 | this.ds_cnt=this.rrd_data.getLongAt(this.ds_cnt_idx);
266 | if (this.ds_cnt<1) {
267 | throw new InvalidRRD("ds count less than 1.");
268 | }
269 |
270 | this.rra_cnt=this.rrd_data.getLongAt(this.rra_cnt_idx);
271 | if (this.ds_cnt<1) {
272 | throw new InvalidRRD("rra count less than 1.");
273 | }
274 |
275 | this.pdp_step=this.rrd_data.getLongAt(this.pdp_step_idx);
276 | if (this.pdp_step<1) {
277 | throw new InvalidRRD("pdp step less than 1.");
278 | }
279 |
280 | // best guess, assuming no weird align problems
281 | this.top_header_size=Math.ceil((this.pdp_step_idx+this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width;
282 | var t=this.rrd_data.getLongAt(this.top_header_size);
283 | if (t==0) {
284 | throw new InvalidRRD("Could not find first DS name.");
285 | }
286 | }
287 |
288 | // Internal, used for initialization
289 | RRDHeader.prototype.calc_idxs = function() {
290 | this.ds_def_idx=this.top_header_size;
291 | // char ds_nam[20], char dst[20], unival par[10]
292 | this.ds_el_size=Math.ceil((20+20)/this.unival_align)*this.unival_align+10*this.unival_width;
293 |
294 | this.rra_def_idx=this.ds_def_idx+this.ds_el_size*this.ds_cnt;
295 | // char cf_nam[20], uint row_cnt, uint pdp_cnt, unival par[10]
296 | this.row_cnt_idx=Math.ceil(20/this.int_align)*this.int_align;
297 | this.rra_def_el_size=Math.ceil((this.row_cnt_idx+2*this.int_width)/this.unival_align)*this.unival_align+10*this.unival_width;
298 |
299 | this.live_head_idx=this.rra_def_idx+this.rra_def_el_size*this.rra_cnt;
300 | // time_t last_up, int last_up_usec
301 | this.live_head_size=2*this.int_width;
302 |
303 | this.pdp_prep_idx=this.live_head_idx+this.live_head_size;
304 | // char last_ds[30], unival scratch[10]
305 | this.pdp_prep_el_size=Math.ceil(30/this.unival_align)*this.unival_align+10*this.unival_width;
306 |
307 | this.cdp_prep_idx=this.pdp_prep_idx+this.pdp_prep_el_size*this.ds_cnt;
308 | // unival scratch[10]
309 | this.cdp_prep_el_size=10*this.unival_width;
310 |
311 | this.rra_ptr_idx=this.cdp_prep_idx+this.cdp_prep_el_size*this.ds_cnt*this.rra_cnt;
312 | // uint cur_row
313 | this.rra_ptr_el_size=1*this.int_width;
314 |
315 | this.header_size=this.rra_ptr_idx+this.rra_ptr_el_size*this.rra_cnt;
316 | }
317 |
318 | // Optional initialization
319 | // Read and calculate row counts
320 | RRDHeader.prototype.load_row_cnts = function() {
321 | this.rra_def_row_cnts=[];
322 | this.rra_def_row_cnt_sums=[]; // how many rows before me
323 | for (var i=0; i=0) && (idx=0) && (idx
2 |
3 |
4 |
5 | RRD Canvas
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | full
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
383 |
384 |
385 |
--------------------------------------------------------------------------------
/js/RrdTime.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010
18 | *
19 | * Convert to javascript: Manuel Sanmartin
20 | **/
21 |
22 | "use strict";
23 |
24 | /**
25 | * RrdTimeError
26 | * @constructor
27 | */
28 | var RrdTimeError = function (message)
29 | {
30 | this.name = "RrdTimeError";
31 | this.message = (message) ? message : "Error";
32 | };
33 | RrdTimeError.prototype = new Error();
34 |
35 | /**
36 | * RrdTime
37 | * @constructor
38 | */
39 | var RrdTime = function(tspec) /* parser */
40 | {
41 | var date = new Date();
42 | var hr = 0;
43 |
44 | this.tspec = tspec;
45 |
46 | this.tokens = (tspec+'').match(/[0-9]+|[A-Za-z]+|[:.+-\/]/g);
47 | this.toklen = this.tokens.length;
48 | this.tokidx = 0;
49 |
50 | this.token = null;
51 | this.tokid = 0;
52 |
53 | this.specials = RrdTime.VARIOUSWORDS;
54 |
55 | /* establish the default time reference */
56 | this.type = RrdTime.ABSOLUTE_TIME;
57 | this.offset = 0;
58 | this.tm_sec = date.getSeconds();
59 | this.tm_min = date.getMinutes();
60 | this.tm_hour = date.getHours();
61 | this.tm_mday = date.getDate();
62 | this.tm_mon = date.getMonth();
63 | this.tm_year = date.getFullYear()-1900;
64 | this.tm_wday = date.getDay();
65 |
66 | this.gettok();
67 | switch (this.tokid) {
68 | case RrdTime.PLUS:
69 | case RrdTime.MINUS:
70 | break; /* jump to OFFSET-SPEC part */
71 | case RrdTime.EPOCH:
72 | this.type = RrdTime.RELATIVE_TO_EPOCH;
73 | /* falls through */
74 | case RrdTime.START:
75 | case RrdTime.END:
76 | if (this.tokid === RrdTime.EPOCH)
77 | this.type = RrdTime.RELATIVE_TO_START_TIME;
78 | else
79 | this.type = RrdTime.RELATIVE_TO_END_TIME;
80 | this.tm_sec = 0;
81 | this.tm_min = 0;
82 | this.tm_hour = 0;
83 | this.tm_mday = 0;
84 | this.tm_mon = 0;
85 | this.tm_year = 0;
86 | /* falls through */
87 | case RrdTime.NOW:
88 | var time_reference = this.tokid;
89 | this.gettok();
90 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS)
91 | break;
92 | if (time_reference != RrdTime.NOW) {
93 | throw new RrdTimeError("'start' or 'end' MUST be followed by +|- offset");
94 | } else if (this.tokid != RrdTime.EOF) {
95 | throw new RrdTimeError("if 'now' is followed by a token it must be +|- offset");
96 | }
97 | break;
98 | case RrdTime.NUMBER: /* Only absolute time specifications below */
99 | var hour_sv = this.tm_hour;
100 | var year_sv = this.tm_year;
101 | this.tm_hour = 30;
102 | this.tm_year = 30000;
103 | this.tod();
104 | this.day();
105 | if (this.tm_hour == 30 && this.tm_year != 30000)
106 | this.tod();
107 | if (this.tm_hour == 30)
108 | this.tm_hour = hour_sv;
109 | if (this.tm_year == 30000)
110 | this.tm_year = year_sv;
111 | break;
112 | case RrdTime.JAN:
113 | case RrdTime.FEB:
114 | case RrdTime.MAR:
115 | case RrdTime.APR:
116 | case RrdTime.MAY:
117 | case RrdTime.JUN:
118 | case RrdTime.JUL:
119 | case RrdTime.AUG:
120 | case RrdTime.SEP:
121 | case RrdTime.OCT:
122 | case RrdTime.NOV:
123 | case RrdTime.DEC:
124 | this.day();
125 | if (this.tokid != RrdTime.NUMBER)
126 | break;
127 | this.tod();
128 | break;
129 | case RrdTime.TEATIME:
130 | hr += 4;
131 | /* falls through */
132 | case RrdTime.NOON:
133 | hr += 12;
134 | /* falls through */
135 | case RrdTime.MIDNIGHT:
136 | this.tm_hour = hr;
137 | this.tm_min = 0;
138 | this.tm_sec = 0;
139 | this.gettok();
140 | this.day();
141 | break;
142 | default:
143 | throw new RrdTimeError("unparsable time: "+this.token+" "+this.sct);
144 | } /* ugly case statement */
145 |
146 | /*
147 | * the OFFSET-SPEC part
148 | * (NOTE, the sc_tokid was prefetched for us by the previous code)
149 | */
150 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) {
151 | this.specials = RrdTime.TIMEMULTIPLIERS; /* switch special words context */
152 | while (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS || this.tokid == RrdTime.NUMBER) {
153 | if (this.tokid == RrdTime.NUMBER) {
154 | this.plus_minus(-1);
155 | } else {
156 | this.plus_minus(this.tokid);
157 | }
158 | this.gettok(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */
159 | }
160 | }
161 |
162 | /* now we should be at EOF */
163 | if (this.tokid != RrdTime.EOF)
164 | throw new RrdTimeError("unparsable trailing text: '..."+this.token+"'");
165 | // if (this.type == RrdTime.ABSOLUTE_TIME)
166 | // if (mktime(&ptv->tm) == -1) // FIXME ??
167 | // panic(e("the specified time is incorrect (out of range?)"));
168 | };
169 |
170 | RrdTime.EOF = -1;
171 | RrdTime.MIDNIGHT = 0;
172 | RrdTime.NOON = 1;
173 | RrdTime.TEATIME = 2;
174 | RrdTime.PM = 3;
175 | RrdTime.AM = 4;
176 | RrdTime.YESTERDAY = 5;
177 | RrdTime.TODAY = 6;
178 | RrdTime.TOMORROW = 7;
179 | RrdTime.NOW = 8;
180 | RrdTime.START = 9;
181 | RrdTime.END = 10;
182 | RrdTime.EPOCH = 11;
183 | RrdTime.SECONDS = 12;
184 | RrdTime.MINUTES = 13;
185 | RrdTime.HOURS = 14;
186 | RrdTime.DAYS = 15;
187 | RrdTime.WEEKS = 16;
188 | RrdTime.MONTHS = 17;
189 | RrdTime.YEARS = 18;
190 | RrdTime.MONTHS_MINUTES = 19;
191 | RrdTime.NUMBER = 20;
192 | RrdTime.PLUS = 21;
193 | RrdTime.MINUS = 22;
194 | RrdTime.DOT = 23;
195 | RrdTime.COLON = 24;
196 | RrdTime.SLASH = 25;
197 | RrdTime.ID = 26;
198 | RrdTime.JUNK = 27;
199 | RrdTime.JAN = 28;
200 | RrdTime.FEB = 29;
201 | RrdTime.MAR = 30;
202 | RrdTime.APR = 31;
203 | RrdTime.MAY = 32;
204 | RrdTime.JUN = 33;
205 | RrdTime.JUL = 34;
206 | RrdTime.AUG = 35;
207 | RrdTime.SEP = 36;
208 | RrdTime.OCT = 37;
209 | RrdTime.NOV = 38;
210 | RrdTime.DEC = 39;
211 | RrdTime.SUN = 40;
212 | RrdTime.MON = 41;
213 | RrdTime.TUE = 42;
214 | RrdTime.WED = 43;
215 | RrdTime.THU = 44;
216 | RrdTime.FRI = 45;
217 | RrdTime.SAT = 46;
218 |
219 | RrdTime.VARIOUSWORDS = {
220 | "midnight": RrdTime.MIDNIGHT, /* 00:00:00 of today or tomorrow */
221 | "noon": RrdTime.NOON, /* 12:00:00 of today or tomorrow */
222 | "teatime": RrdTime.TEATIME, /* 16:00:00 of today or tomorrow */
223 | "am": RrdTime.AM, /* morning times for 0-12 clock */
224 | "pm": RrdTime.PM, /* evening times for 0-12 clock */
225 | "tomorrow": RrdTime.TOMORROW,
226 | "yesterday": RrdTime.YESTERDAY,
227 | "today": RrdTime.TODAY,
228 | "now": RrdTime.NOW,
229 | "n": RrdTime.NOW,
230 | "start": RrdTime.START,
231 | "s": RrdTime.START,
232 | "end": RrdTime.END,
233 | "e": RrdTime.END,
234 | "epoch": RrdTime.EPOCH,
235 | "jan": RrdTime.JAN,
236 | "feb": RrdTime.FEB,
237 | "mar": RrdTime.MAR,
238 | "apr": RrdTime.APR,
239 | "may": RrdTime.MAY,
240 | "jun": RrdTime.JUN,
241 | "jul": RrdTime.JUL,
242 | "aug": RrdTime.AUG,
243 | "sep": RrdTime.SEP,
244 | "oct": RrdTime.OCT,
245 | "nov": RrdTime.NOV,
246 | "dec": RrdTime.DEC,
247 | "january": RrdTime.JAN,
248 | "february": RrdTime.FEB,
249 | "march": RrdTime.MAR,
250 | "april": RrdTime.APR,
251 | // "may": RrdTime.MAY,
252 | "june": RrdTime.JUN,
253 | "july": RrdTime.JUL,
254 | "august": RrdTime.AUG,
255 | "september": RrdTime.SEP,
256 | "october": RrdTime.OCT,
257 | "november": RrdTime.NOV,
258 | "december": RrdTime.DEC,
259 | "sunday": RrdTime.SUN,
260 | "sun": RrdTime.SUN,
261 | "monday": RrdTime.MON,
262 | "mon": RrdTime.MON,
263 | "tuesday": RrdTime.TUE,
264 | "tue": RrdTime.TUE,
265 | "wednesday": RrdTime.WED,
266 | "wed": RrdTime.WED,
267 | "thursday": RrdTime.THU,
268 | "thu": RrdTime.THU,
269 | "friday": RrdTime.FRI,
270 | "fri": RrdTime.FRI,
271 | "saturday": RrdTime.SAT,
272 | "sat": RrdTime.SAT
273 | };
274 |
275 | RrdTime.TIMEMULTIPLIERS = {
276 | "second": RrdTime.SECONDS, /* seconds multiplier */
277 | "seconds": RrdTime.SECONDS, /* (pluralized) */
278 | "sec": RrdTime.SECONDS, /* (generic) */
279 | "s": RrdTime.SECONDS, /* (short generic) */
280 | "minute": RrdTime.MINUTES, /* minutes multiplier */
281 | "minutes": RrdTime.MINUTES, /* (pluralized) */
282 | "min": RrdTime.MINUTES, /* (generic) */
283 | "m": RrdTime.MONTHS_MINUTES, /* (short generic) */
284 | "hour": RrdTime.HOURS, /* hours ... */
285 | "hours": RrdTime.HOURS, /* (pluralized) */
286 | "hr": RrdTime.HOURS, /* (generic) */
287 | "h": RrdTime.HOURS, /* (short generic) */
288 | "day": RrdTime.DAYS, /* days ... */
289 | "days": RrdTime.DAYS, /* (pluralized) */
290 | "d": RrdTime.DAYS, /* (short generic) */
291 | "week": RrdTime.WEEKS, /* week ... */
292 | "weeks": RrdTime.WEEKS, /* (pluralized) */
293 | "wk": RrdTime.WEEKS, /* (generic) */
294 | "w": RrdTime.WEEKS, /* (short generic) */
295 | "month": RrdTime.MONTHS, /* week ... */
296 | "months": RrdTime.MONTHS, /* (pluralized) */
297 | "mon": RrdTime.MONTHS, /* (generic) */
298 | "year": RrdTime.YEARS, /* year ... */
299 | "years": RrdTime.YEARS, /* (pluralized) */
300 | "yr": RrdTime.YEARS, /* (generic) */
301 | "y": RrdTime.YEARS /* (short generic) */
302 | };
303 |
304 | RrdTime.ABSOLUTE_TIME = 0;
305 | RrdTime.RELATIVE_TO_START_TIME = 1;
306 | RrdTime.RELATIVE_TO_END_TIME = 2;
307 | RrdTime.RELATIVE_TO_EPOCH = 3;
308 |
309 | RrdTime.prototype.gettok = function ()
310 | {
311 | if (this.tokidx >= this.toklen) {
312 | this.tokid = RrdTime.EOF;
313 | } else {
314 | this.token = this.tokens[this.tokidx];
315 | this.tokidx++;
316 | if (!isNaN(this.token)) {
317 | this.tokid = RrdTime.NUMBER;
318 | this.token = parseInt(this.token, 10);
319 | } else if (this.token === ':') {
320 | this.tokid = RrdTime.COLON;
321 | } else if (this.token === '.') {
322 | this.tokid = RrdTime.DOT;
323 | } else if (this.token === '+') {
324 | this.tokid = RrdTime.PLUS;
325 | } else if (this.token === '/') {
326 | this.tokid = RrdTime.SLASH;
327 | } else if (this.token === '-') {
328 | this.tokid = RrdTime.MINUS;
329 | } else {
330 | this.tokid = RrdTime.ID;
331 | if (this.token in this.specials)
332 | this.tokid = this.specials[this.token];
333 | }
334 | }
335 | return this.tokid;
336 | };
337 |
338 | RrdTime.prototype.plus_minus = function (doop)
339 | {
340 | var op = RrdTime.PLUS;
341 | var prev_multiplier = -1;
342 | var delta;
343 |
344 | if (doop >= 0) {
345 | op = doop;
346 | if (this.gettok() != RrdTime.NUMBER)
347 | throw new RrdTimeError("There should be number after '"+(op == RrdTime.PLUS ? '+' : '-')+"'");
348 | prev_multiplier = -1; /* reset months-minutes guessing mechanics */
349 | }
350 | /* if doop is < 0 then we repeat the previous op with the prefetched number */
351 | delta = this.token;
352 | if (this.gettok() == RrdTime.MONTHS_MINUTES) {
353 | /* hard job to guess what does that -5m means: -5mon or -5min? */
354 | switch (prev_multiplier) {
355 | case RrdTime.DAYS:
356 | case RrdTime.WEEKS:
357 | case RrdTime.MONTHS:
358 | case RrdTime.YEARS:
359 | this.tokid = RrdTime.MONTHS;
360 | break;
361 | case RrdTime.SECONDS:
362 | case RrdTime.MINUTES:
363 | case RrdTime.HOURS:
364 | this.tokid = RrdTime.MINUTES;
365 | break;
366 | default:
367 | if (delta < 6) /* it may be some other value but in the context of RRD who needs less than 6 min deltas? */
368 | this.tokid = RrdTime.MONTHS;
369 | else
370 | this.tokid = RrdTime.MINUTES;
371 | }
372 | }
373 | prev_multiplier = this.tokid;
374 | switch (this.tokid) {
375 | case RrdTime.YEARS:
376 | this.tm_year += ( op == RrdTime.PLUS) ? delta : -delta;
377 | return;
378 | case RrdTime.MONTHS:
379 | this.tm_mon += ( op == RrdTime.PLUS) ? delta : -delta;
380 | return;
381 | case RrdTime.WEEKS:
382 | delta *= 7;
383 | /* falls through */
384 | case RrdTime.DAYS:
385 | this.tm_mday += ( op == RrdTime.PLUS) ? delta : -delta;
386 | return;
387 | case RrdTime.HOURS:
388 | this.offset += (op == RrdTime.PLUS) ? delta * 60 * 60 : -delta * 60 * 60;
389 | return;
390 | case RrdTime.MINUTES:
391 | this.offset += (op == RrdTime.PLUS) ? delta * 60 : -delta * 60;
392 | return;
393 | case RrdTime.SECONDS:
394 | this.offset += (op == RrdTime.PLUS) ? delta : -delta;
395 | return;
396 | default: /*default unit is seconds */
397 | this.offset += (op == RrdTime.PLUS) ? delta : -delta;
398 | return;
399 | }
400 | throw new RrdTimeError("well-known time unit expected after "+delta);
401 | };
402 |
403 | RrdTime.prototype.tod = function() /* tod() computes the time of day (TIME-OF-DAY-SPEC) */
404 | {
405 | var hour, minute = 0;
406 | var tlen;
407 | /* save token status in case we must abort */
408 | var tokid_sv = this.tokid;
409 |
410 | tlen = (this.token+"").length;
411 | /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */
412 | if (tlen > 2)
413 | return;
414 | hour = this.token;
415 | this.gettok();
416 | if (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT) {
417 | /* guess we are looking at a date */
418 | this.tokid = tokid_sv;
419 | this.token = hour;
420 | return;
421 | }
422 | if (this.tokid == RrdTime.COLON) {
423 | if (this.gettok() != RrdTime.NUMBER)
424 | throw new RrdTimeError("Parsing HH:MM syntax, expecting MM as number, got none");
425 | minute = this.token;
426 | if (minute > 59)
427 | throw new RrdTimeError("parsing HH:MM syntax, got MM = "+minute+" (>59!)");
428 | this.gettok();
429 | }
430 | /* check if an AM or PM specifier was given */
431 | if (this.tokid == RrdTime.AM || this.tokid == RrdTime.PM) {
432 | if (hour > 12) {
433 | throw new RrdTimeError("there cannot be more than 12 AM or PM hours");
434 | }
435 | if (this.tokid == RrdTime.PM) {
436 | if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
437 | hour += 12;
438 | } else {
439 | if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
440 | hour = 0;
441 | }
442 | this.gettok();
443 | } else if (hour > 23) {
444 | /* guess it was not a time then ... */
445 | this.tokid = tokid_sv;
446 | this.token = hour;
447 | return;
448 | }
449 | this.tm_hour = hour;
450 | this.tm_min = minute;
451 | this.tm_sec = 0;
452 | if (this.tm_hour == 24) {
453 | this.tm_hour = 0;
454 | this.tm_mday++;
455 | }
456 | };
457 |
458 | RrdTime.prototype.assign_date = function(mday, mon, year)
459 | {
460 | if (year > 138) {
461 | if (year > 1970) {
462 | year -= 1900;
463 | } else {
464 | throw new RrdTimeError("invalid year "+year+" (should be either 00-99 or >1900)");
465 | }
466 | } else if (year >= 0 && year < 38) {
467 | year += 100; /* Allow year 2000-2037 to be specified as */
468 | }
469 | /* 00-37 until the problem of 2038 year will */
470 | /* arise for unices with 32-bit time_t :) */
471 | if (year < 70)
472 | throw new RrdTimeError("won't handle dates before epoch (01/01/1970), sorry");
473 |
474 | this.tm_mday = mday;
475 | this.tm_mon = mon;
476 | this.tm_year = year;
477 | };
478 |
479 | RrdTime.prototype.day = function ()
480 | {
481 | var mday = 0, wday, mon, year = this.tm_year;
482 |
483 | switch (this.tokid) {
484 | case RrdTime.YESTERDAY:
485 | this.tm_mday--;
486 | /* falls through */
487 | case RrdTime.TODAY:
488 | this.gettok();
489 | break;
490 | case RrdTime.TOMORROW:
491 | this.tm_mday++;
492 | this.gettok();
493 | break;
494 | case RrdTime.JAN:
495 | case RrdTime.FEB:
496 | case RrdTime.MAR:
497 | case RrdTime.APR:
498 | case RrdTime.MAY:
499 | case RrdTime.JUN:
500 | case RrdTime.JUL:
501 | case RrdTime.AUG:
502 | case RrdTime.SEP:
503 | case RrdTime.OCT:
504 | case RrdTime.NOV:
505 | case RrdTime.DEC:
506 | mon = (this.tokid - RrdTime.JAN);
507 | if (this.gettok() != RrdTime.NUMBER)
508 | throw new RrdTimeError("the day of the month should follow month name");
509 | mday = this.token;
510 | if (this.gettok() == RrdTime.NUMBER) {
511 | year = this.token;
512 | this.gettok();
513 | } else {
514 | year = this.tm_year;
515 | }
516 | this.assign_date(mday, mon, year);
517 | break;
518 | case RrdTime.SUN:
519 | case RrdTime.MON:
520 | case RrdTime.TUE:
521 | case RrdTime.WED:
522 | case RrdTime.THU:
523 | case RrdTime.FRI:
524 | case RrdTime.SAT:
525 | wday = (this.tokid - RrdTime.SUN);
526 | this.tm_mday += (wday - this.tm_wday);
527 | this.gettok();
528 | break;
529 | case RrdTime.NUMBER:
530 | mon = this.token;
531 | if (mon > 10 * 365 * 24 * 60 * 60) {
532 | this.localtime(mon);
533 | this.gettok();
534 | break;
535 | }
536 | if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
537 | var str = this.token + '';
538 | year = parseInt(str.substr(0,4),10);
539 | mon = parseInt(str.substr(4,2),10);
540 | mday = parseInt(str.substr(6,2),10);
541 | this.gettok();
542 | } else {
543 | this.gettok();
544 | if (mon <= 31 && (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT)) {
545 | var sep = this.tokid;
546 | if (this.gettok() != RrdTime.NUMBER)
547 | throw new RrdTimeError("there should be "+(RrdTime.DOT ? "month" : "day")+" number after '"+(RrdTime.DOT ? '.' : '/')+"'");
548 | mday = this.token;
549 | if (this.gettok() == sep) {
550 | if (this.gettok() != RrdTime.NUMBER)
551 | throw new RrdTimeError("there should be year number after '"+(sep == RrdTime.DOT ? '.' : '/')+"'");
552 | year = this.token;
553 | this.gettok();
554 | }
555 | if (sep == RrdTime.DOT) {
556 | var x = mday;
557 | mday = mon;
558 | mon = x;
559 | }
560 | }
561 | }
562 | mon--;
563 | if (mon < 0 || mon > 11)
564 | throw new RrdTimeError("did you really mean month "+(mon+1)+"?");
565 | if (mday < 1 || mday > 31)
566 | throw new RrdTimeError("I'm afraid that "+mday+" is not a valid day of the month");
567 | this.assign_date(mday, mon, year);
568 | break;
569 | }
570 | };
571 |
572 | RrdTime.prototype.localtime = function (tm)
573 | {
574 | var date = new Date(tm*1000);
575 | this.tm_sec = date.getSeconds();
576 | this.tm_min = date.getMinutes();
577 | this.tm_hour = date.getHours();
578 | this.tm_mday = date.getDate();
579 | this.tm_mon = date.getMonth();
580 | this.tm_year = date.getFullYear()-1900;
581 | this.tm_wday = date.getDay();
582 | };
583 |
584 | RrdTime.prototype.mktime = function()
585 | {
586 | var date = new Date(this.tm_year+1900, this.tm_mon, this.tm_mday, this.tm_hour, this.tm_min, this.tm_sec);
587 | return Math.round(date.getTime()/1000.0);
588 | };
589 |
590 | RrdTime.proc_start_end = function(start_t, end_t)
591 | {
592 | var start, end;
593 |
594 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME && end_t.type == RrdTime.RELATIVE_TO_START_TIME)
595 | throw new RrdTimeError("the start and end times cannot be specified relative to each other");
596 | if (start_t.type == RrdTime.RELATIVE_TO_START_TIME)
597 | throw new RrdTimeError("the start time cannot be specified relative to itself");
598 | if (end_t.type == RrdTime.RELATIVE_TO_END_TIME)
599 | throw new RrdTimeError("the end time cannot be specified relative to itself");
600 |
601 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME) {
602 | end = end_t.mktime() + end_t.offset;
603 | var tmtmp = new Date(end*1000);
604 | tmtmp.setDate(tmtmp.getDate()+start_t.tm_mday);
605 | tmtmp.setMonth(tmtmp.getMonth()+start_t.tm_mon);
606 | tmtmp.setFullYear(tmtmp.getFullYear()+start_t.tm_year);
607 | start = Math.round(tmtmp.getTime()/1000.0) + start_t.offset;
608 | } else {
609 | start = start_t.mktime() + start_t.offset;
610 | }
611 |
612 | if (end_t.type == RrdTime.RELATIVE_TO_START_TIME) {
613 | start = start_t.mktime() + start_t.offset;
614 | var tmtmp = new Date(start*1000);
615 | tmtmp.setDate(tmtmp.getDate()+end_t.tm_mday);
616 | tmtmp.setMonth(tmtmp.getMonth()+end_t.tm_mon);
617 | tmtmp.setFullYear(tmtmp.getFullYear()+end_t.tm_year);
618 | end = Math.round(tmtmp.getTime()/1000.0) + end_t.offset;
619 | } else {
620 | end = end_t.mktime() + end_t.offset;
621 | }
622 |
623 | return [start, end];
624 | };
625 |
626 |
--------------------------------------------------------------------------------
/js/RrdRpn.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010
18 | *
19 | * Convert to javascript: Manuel Sanmartin
20 | **/
21 |
22 | "use strict";
23 |
24 | /**
25 | * RrdRpnError
26 | * @constructor
27 | */
28 | var RrdRpnError = function (message)
29 | {
30 | this.name = "RrdRpnError";
31 | this.message = (message) ? message : "RPN stack underflow";
32 | };
33 | RrdRpnError.prototype = new Error();
34 |
35 | /**
36 | * RrdRpn
37 | * @constructor
38 | */
39 | var RrdRpn = function (str_expr, gdes) /* parser */
40 | {
41 | var steps = -1;
42 | var expr;
43 | var exprs = str_expr.split(',');
44 |
45 | this.rpnexpr = str_expr;
46 | this.rpnp = [];
47 | this.rpnstack = null;
48 |
49 | for(var i=0, len=exprs.length; i < len; i++) {
50 | expr=exprs[i].toUpperCase();
51 |
52 | steps++;
53 | this.rpnp[steps] = {};
54 |
55 | if (!isNaN(expr)) {
56 | this.rpnp[steps].op = RrdRpn.OP_NUMBER;
57 | this.rpnp[steps].val = parseFloat(expr);
58 | }
59 | else if (expr === '+') this.rpnp[steps].op = RrdRpn.OP_ADD;
60 | else if (expr === '-') this.rpnp[steps].op = RrdRpn.OP_SUB;
61 | else if (expr === '*') this.rpnp[steps].op = RrdRpn.OP_MUL;
62 | else if (expr === '/') this.rpnp[steps].op = RrdRpn.OP_DIV;
63 | else if (expr === '%') this.rpnp[steps].op = RrdRpn.OP_MOD;
64 | else if (expr === 'SIN') this.rpnp[steps].op = RrdRpn.OP_SIN;
65 | else if (expr === 'COS') this.rpnp[steps].op = RrdRpn.OP_COS;
66 | else if (expr === 'LOG') this.rpnp[steps].op = RrdRpn.OP_LOG;
67 | else if (expr === 'FLOOR') this.rpnp[steps].op = RrdRpn.OP_FLOOR;
68 | else if (expr === 'CEIL') this.rpnp[steps].op = RrdRpn.OP_CEIL;
69 | else if (expr === 'EXP') this.rpnp[steps].op = RrdRpn.OP_EXP;
70 | else if (expr === 'DUP') this.rpnp[steps].op = RrdRpn.OP_DUP;
71 | else if (expr === 'EXC') this.rpnp[steps].op = RrdRpn.OP_EXC;
72 | else if (expr === 'POP') this.rpnp[steps].op = RrdRpn.OP_POP;
73 | else if (expr === 'LTIME') this.rpnp[steps].op = RrdRpn.OP_LTIME;
74 | else if (expr === 'LT') this.rpnp[steps].op = RrdRpn.OP_LT;
75 | else if (expr === 'LE') this.rpnp[steps].op = RrdRpn.OP_LE;
76 | else if (expr === 'GT') this.rpnp[steps].op = RrdRpn.OP_GT;
77 | else if (expr === 'GE') this.rpnp[steps].op = RrdRpn.OP_GE;
78 | else if (expr === 'EQ') this.rpnp[steps].op = RrdRpn.OP_EQ;
79 | else if (expr === 'IF') this.rpnp[steps].op = RrdRpn.OP_IF;
80 | else if (expr === 'MIN') this.rpnp[steps].op = RrdRpn.OP_MIN;
81 | else if (expr === 'MAX') this.rpnp[steps].op = RrdRpn.OP_MAX;
82 | else if (expr === 'LIMIT') this.rpnp[steps].op = RrdRpn.OP_LIMIT;
83 | else if (expr === 'UNKN') this.rpnp[steps].op = RrdRpn.OP_UNKN;
84 | else if (expr === 'UN') this.rpnp[steps].op = RrdRpn.OP_UN;
85 | else if (expr === 'NEGINF') this.rpnp[steps].op = RrdRpn.OP_NEGINF;
86 | else if (expr === 'NE') this.rpnp[steps].op = RrdRpn.OP_NE;
87 | else if (expr === 'COUNT') this.rpnp[steps].op = RrdRpn.OP_COUNT;
88 | else if (/PREV\([-_A-Za-z0-9]+\)/.test(expr)) {
89 | var match = exprs[i].match(/PREV\(([-_A-Za-z0-9]+)\)/i);
90 | if (match.length == 2) {
91 | this.rpnp[steps].op = RrdRpn.OP_PREV_OTHER;
92 | this.rpnp[steps].ptr = this.find_var(gdes, match[1]); // FIXME if -1
93 | }
94 | }
95 | else if (expr === 'PREV') this.rpnp[steps].op = RrdRpn.OP_PREV;
96 | else if (expr === 'INF') this.rpnp[steps].op = RrdRpn.OP_INF;
97 | else if (expr === 'ISINF') this.rpnp[steps].op = RrdRpn.OP_ISINF;
98 | else if (expr === 'NOW') this.rpnp[steps].op = RrdRpn.OP_NOW;
99 | else if (expr === 'TIME') this.rpnp[steps].op = RrdRpn.OP_TIME;
100 | else if (expr === 'ATAN2') this.rpnp[steps].op = RrdRpn.OP_ATAN2;
101 | else if (expr === 'ATAN') this.rpnp[steps].op = RrdRpn.OP_ATAN;
102 | else if (expr === 'SQRT') this.rpnp[steps].op = RrdRpn.OP_SQRT;
103 | else if (expr === 'SORT') this.rpnp[steps].op = RrdRpn.OP_SORT;
104 | else if (expr === 'REV') this.rpnp[steps].op = RrdRpn.OP_REV;
105 | else if (expr === 'TREND') this.rpnp[steps].op = RrdRpn.OP_TREND;
106 | else if (expr === 'TRENDNAN') this.rpnp[steps].op = RrdRpn.OP_TRENDNAN;
107 | else if (expr === 'PREDICT') this.rpnp[steps].op = RrdRpn.OP_PREDICT;
108 | else if (expr === 'PREDICTSIGMA') this.rpnp[steps].op = RrdRpn.OP_PREDICTSIGMA;
109 | else if (expr === 'RAD2DEG') this.rpnp[steps].op = RrdRpn.OP_RAD2DEG;
110 | else if (expr === 'DEG2RAD') this.rpnp[steps].op = RrdRpn.OP_DEG2RAD;
111 | else if (expr === 'AVG') this.rpnp[steps].op = RrdRpn.OP_AVG;
112 | else if (expr === 'ABS') this.rpnp[steps].op = RrdRpn.OP_ABS;
113 | else if (expr === 'ADDNAN') this.rpnp[steps].op = RrdRpn.OP_ADDNAN;
114 | else if (/[-_A-Za-z0-9]+/.test(expr)) {
115 | this.rpnp[steps].ptr = this.find_var(gdes, exprs[i]); // FIXME if -1
116 | this.rpnp[steps].op = RrdRpn.OP_VARIABLE;
117 | } else {
118 | return;
119 | }
120 | }
121 | this.rpnp[steps + 1] = {};
122 | this.rpnp[steps + 1].op = RrdRpn.OP_END;
123 | };
124 |
125 | RrdRpn.OP_NUMBER= 0;
126 | RrdRpn.OP_VARIABLE = 1;
127 | RrdRpn.OP_INF = 2;
128 | RrdRpn.OP_PREV = 3;
129 | RrdRpn.OP_NEGINF = 4;
130 | RrdRpn.OP_UNKN = 5;
131 | RrdRpn.OP_NOW = 6;
132 | RrdRpn.OP_TIME = 7;
133 | RrdRpn.OP_ADD = 8;
134 | RrdRpn.OP_MOD = 9;
135 | RrdRpn.OP_SUB = 10;
136 | RrdRpn.OP_MUL = 11;
137 | RrdRpn.OP_DIV = 12;
138 | RrdRpn.OP_SIN = 13;
139 | RrdRpn.OP_DUP = 14;
140 | RrdRpn.OP_EXC = 15;
141 | RrdRpn.OP_POP = 16;
142 | RrdRpn.OP_COS = 17;
143 | RrdRpn.OP_LOG = 18;
144 | RrdRpn.OP_EXP = 19;
145 | RrdRpn.OP_LT = 20;
146 | RrdRpn.OP_LE = 21;
147 | RrdRpn.OP_GT = 22;
148 | RrdRpn.OP_GE = 23;
149 | RrdRpn.OP_EQ = 24;
150 | RrdRpn.OP_IF = 25;
151 | RrdRpn.OP_MIN = 26;
152 | RrdRpn.OP_MAX = 27;
153 | RrdRpn.OP_LIMIT = 28;
154 | RrdRpn.OP_FLOOR = 29;
155 | RrdRpn.OP_CEIL = 30;
156 | RrdRpn.OP_UN = 31;
157 | RrdRpn.OP_END = 32;
158 | RrdRpn.OP_LTIME = 33;
159 | RrdRpn.OP_NE = 34;
160 | RrdRpn.OP_ISINF = 35;
161 | RrdRpn.OP_PREV_OTHER = 36;
162 | RrdRpn.OP_COUNT = 37;
163 | RrdRpn.OP_ATAN = 38;
164 | RrdRpn.OP_SQRT = 39;
165 | RrdRpn.OP_SORT = 40;
166 | RrdRpn.OP_REV = 41;
167 | RrdRpn.OP_TREND = 42;
168 | RrdRpn.OP_TRENDNAN = 43;
169 | RrdRpn.OP_ATAN2 = 44;
170 | RrdRpn.OP_RAD2DEG = 45;
171 | RrdRpn.OP_DEG2RAD = 46;
172 | RrdRpn.OP_PREDICT = 47;
173 | RrdRpn.OP_PREDICTSIGMA = 48;
174 | RrdRpn.OP_AVG = 49;
175 | RrdRpn.OP_ABS = 50;
176 | RrdRpn.OP_ADDNAN = 51 ;
177 |
178 | RrdRpn.prototype.find_var = function(gdes, key)
179 | {
180 | for (var ii = 0, gdes_c = gdes.length; ii < gdes_c; ii++) {
181 | if ((gdes[ii].gf == RrdGraphDesc.GF_DEF ||
182 | gdes[ii].gf == RrdGraphDesc.GF_VDEF ||
183 | gdes[ii].gf == RrdGraphDesc.GF_CDEF)
184 | && gdes[ii].vname == key) {
185 | return ii;
186 | }
187 | }
188 | return -1;
189 | };
190 |
191 | RrdRpn.prototype.compare_double = function(x, y)
192 | {
193 | var diff = x - y;
194 | return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
195 | };
196 |
197 | RrdRpn.prototype.fmod = function (x, y)
198 | {
199 | // http://kevin.vanzonneveld.net
200 | // + original by: Onno Marsman
201 | // + input by: Brett Zamir (http://brett-zamir.me)
202 | // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
203 | // * example 1: fmod(5.7, 1.3);
204 | // * returns 1: 0.5
205 | var tmp, tmp2, p = 0,
206 | pY = 0,
207 | l = 0.0,
208 | l2 = 0.0;
209 |
210 | tmp = x.toExponential().match(/^.\.?(.*)e(.+)$/);
211 | p = parseInt(tmp[2], 10) - (tmp[1] + '').length;
212 | tmp = y.toExponential().match(/^.\.?(.*)e(.+)$/);
213 | pY = parseInt(tmp[2], 10) - (tmp[1] + '').length;
214 |
215 | if (pY > p) p = pY;
216 |
217 | tmp2 = (x % y);
218 |
219 | if (p < -100 || p > 20) {
220 | l = Math.round(Math.log(tmp2) / Math.log(10));
221 | l2 = Math.pow(10, l);
222 | return (tmp2 / l2).toFixed(l - p) * l2;
223 | } else {
224 | return parseFloat(tmp2.toFixed(-p));
225 | }
226 | };
227 |
228 | RrdRpn.prototype.calc = function (data_idx, output, output_idx)
229 | {
230 | var stptr = -1;
231 |
232 | this.rpnstack = [];
233 |
234 | for (var rpi = 0; this.rpnp[rpi].op != RrdRpn.OP_END; rpi++) {
235 | switch (this.rpnp[rpi].op) {
236 | case RrdRpn.OP_NUMBER:
237 | this.rpnstack[++stptr] = this.rpnp[rpi].val;
238 | break;
239 | case RrdRpn.OP_VARIABLE:
240 | case RrdRpn.OP_PREV_OTHER:
241 | if (this.rpnp[rpi].ds_cnt == 0) {
242 | throw new RrdRpnError("VDEF made it into rpn_calc... aborting");
243 | } else {
244 | if (this.rpnp[rpi].op == RrdRpn.OP_VARIABLE) {
245 | this.rpnstack[++stptr] = this.rpnp[rpi].data[this.rpnp[rpi].pdata];
246 | } else {
247 | if ((output_idx) <= 0) this.rpnstack[++stptr] = Number.NaN;
248 | else this.rpnstack[++stptr] = this.rpnp[rpi].data[this.rpnp[rpi].pdata - this.rpnp[rpi].ds_cnt];
249 | }
250 | if (data_idx % this.rpnp[rpi].step == 0) {
251 | this.rpnp[rpi].pdata += this.rpnp[rpi].ds_cnt;
252 | }
253 | }
254 | break;
255 | case RrdRpn.OP_COUNT:
256 | this.rpnstack[++stptr] = (output_idx + 1); /* Note: Counter starts at 1 */
257 | break;
258 | case RrdRpn.OP_PREV:
259 | if ((output_idx) <= 0) this.rpnstack[++stptr] = Number.NaN;
260 | else this.rpnstack[++stptr] = output[output_idx - 1];
261 | break;
262 | case RrdRpn.OP_UNKN:
263 | this.rpnstack[++stptr] = Number.NaN;
264 | break;
265 | case RrdRpn.OP_INF:
266 | this.rpnstack[++stptr] = Infinity;
267 | break;
268 | case RrdRpn.OP_NEGINF:
269 | this.rpnstack[++stptr] = -Infinity;
270 | break;
271 | case RrdRpn.OP_NOW:
272 | this.rpnstack[++stptr] = Math.round((new Date()).getTime() / 1000);
273 | break;
274 | case RrdRpn.OP_TIME:
275 | this.rpnstack[++stptr] = data_idx;
276 | break;
277 | case RrdRpn.OP_LTIME:
278 | var date = new Date(data_idx*1000); // FIXME XXX
279 | this.rpnstack[++stptr] = date.getTimezoneOffset() * 60 + data_idx;
280 | break;
281 | case RrdRpn.OP_ADD:
282 | if(stptr < 1) throw new RrdRpnError();
283 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] + this.rpnstack[stptr];
284 | stptr--;
285 | break;
286 | case RrdRpn.OP_ADDNAN:
287 | if(stptr < 1) throw new RrdRpnError();
288 | if (isNaN(this.rpnstack[stptr - 1])) {
289 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
290 | } else if (isNaN(this.rpnstack[stptr])) {
291 | /* NOOP */
292 | /* this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1]; */
293 | } else {
294 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] + this.rpnstack[stptr];
295 | }
296 | stptr--;
297 | break;
298 | case RrdRpn.OP_SUB:
299 | if(stptr < 1) throw new RrdRpnError();
300 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] - this.rpnstack[stptr];
301 | stptr--;
302 | break;
303 | case RrdRpn.OP_MUL:
304 | if(stptr < 1) throw new RrdRpnError();
305 | this.rpnstack[stptr - 1] = (this.rpnstack[stptr - 1]) * (this.rpnstack[stptr]);
306 | stptr--;
307 | break;
308 | case RrdRpn.OP_DIV:
309 | if(stptr < 1) throw new RrdRpnError();
310 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] / this.rpnstack[stptr];
311 | stptr--;
312 | break;
313 | case RrdRpn.OP_MOD:
314 | if(stptr < 1) throw new RrdRpnError();
315 | this.rpnstack[stptr - 1] = this.fmod(this.rpnstack[stptr - 1] , this.rpnstack[stptr]);
316 | stptr--;
317 | break;
318 | case RrdRpn.OP_SIN:
319 | if(stptr < 0) throw new RrdRpnError();
320 | this.rpnstack[stptr] = Math.sin(this.rpnstack[stptr]);
321 | break;
322 | case RrdRpn.OP_ATAN:
323 | if(stptr < 0) throw new RrdRpnError();
324 | this.rpnstack[stptr] = Math.atan(this.rpnstack[stptr]);
325 | break;
326 | case RrdRpn.OP_RAD2DEG:
327 | if(stptr < 0) throw new RrdRpnError();
328 | this.rpnstack[stptr] = 57.29577951 * this.rpnstack[stptr];
329 | break;
330 | case RrdRpn.OP_DEG2RAD:
331 | if(stptr < 0) throw new RrdRpnError();
332 | this.rpnstack[stptr] = 0.0174532952 * this.rpnstack[stptr];
333 | break;
334 | case RrdRpn.OP_ATAN2:
335 | if(stptr < 1) throw new RrdRpnError();
336 | this.rpnstack[stptr - 1] = Math.atan2(this.rpnstack[stptr - 1], this.rpnstack[stptr]);
337 | stptr--;
338 | break;
339 | case RrdRpn.OP_COS:
340 | if(stptr < 0) throw new RrdRpnError();
341 | this.rpnstack[stptr] = Math.cos(this.rpnstack[stptr]);
342 | break;
343 | case RrdRpn.OP_CEIL:
344 | if(stptr < 0) throw new RrdRpnError();
345 | this.rpnstack[stptr] = Math.ceil(this.rpnstack[stptr]);
346 | break;
347 | case RrdRpn.OP_FLOOR:
348 | if(stptr < 0) throw new RrdRpnError();
349 | this.rpnstack[stptr] = Math.floor(this.rpnstack[stptr]);
350 | break;
351 | case RrdRpn.OP_LOG:
352 | if(stptr < 0) throw new RrdRpnError();
353 | this.rpnstack[stptr] = Math.log(this.rpnstack[stptr]);
354 | break;
355 | case RrdRpn.OP_DUP:
356 | if(stptr < 0) throw new RrdRpnError();
357 | this.rpnstack[stptr + 1] = this.rpnstack[stptr];
358 | stptr++;
359 | break;
360 | case RrdRpn.OP_POP:
361 | if(stptr < 0) throw new RrdRpnError();
362 | stptr--;
363 | break;
364 | case RrdRpn.OP_EXC:
365 | if(stptr < 1) throw new RrdRpnError(); {
366 | var dummy = this.rpnstack[stptr];
367 | this.rpnstack[stptr] = this.rpnstack[stptr - 1];
368 | this.rpnstack[stptr - 1] = dummy;
369 | }
370 | break;
371 | case RrdRpn.OP_EXP:
372 | if(stptr < 0) throw new RrdRpnError();
373 | this.rpnstack[stptr] = Math.exp(this.rpnstack[stptr]);
374 | break;
375 | case RrdRpn.OP_LT:
376 | if(stptr < 1) throw new RrdRpnError();
377 | if (isNaN(this.rpnstack[stptr - 1])) {
378 | } else if (isNaN(this.rpnstack[stptr])) {
379 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
380 | } else {
381 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] < this.rpnstack[stptr] ? 1.0 : 0.0;
382 | }
383 | stptr--;
384 | break;
385 | case RrdRpn.OP_LE:
386 | if(stptr < 1) throw new RrdRpnError();
387 | if (isNaN(this.rpnstack[stptr - 1])) {
388 | } else if (isNaN(this.rpnstack[stptr])) {
389 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
390 | } else {
391 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] <= this.rpnstack[stptr] ? 1.0 : 0.0;
392 | }
393 | stptr--;
394 | break;
395 | case RrdRpn.OP_GT:
396 | if(stptr < 1) throw new RrdRpnError();
397 | if (isNaN(this.rpnstack[stptr - 1])) {
398 | } else if (isNaN(this.rpnstack[stptr])) {
399 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
400 | } else {
401 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] > this.rpnstack[stptr] ? 1.0 : 0.0;
402 | }
403 | stptr--;
404 | break;
405 | case RrdRpn.OP_GE:
406 | if(stptr < 1) throw new RrdRpnError();
407 | if (isNaN(this.rpnstack[stptr - 1])) {
408 | } else if (isNaN(this.rpnstack[stptr])) {
409 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
410 | } else {
411 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] >= this.rpnstack[stptr] ? 1.0 : 0.0;
412 | }
413 | stptr--;
414 | break;
415 | case RrdRpn.OP_NE:
416 | if(stptr < 1) throw new RrdRpnError();
417 | if (isNaN(this.rpnstack[stptr - 1])) {
418 | } else if (isNaN(this.rpnstack[stptr])) {
419 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
420 | } else {
421 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] == this.rpnstack[stptr] ? 0.0 : 1.0;
422 | }
423 | stptr--;
424 | break;
425 | case RrdRpn.OP_EQ:
426 | if(stptr < 1) throw new RrdRpnError();
427 | if (isNaN(this.rpnstack[stptr - 1])) {
428 | } else if (isNaN(this.rpnstack[stptr])) {
429 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
430 | } else {
431 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] == this.rpnstack[stptr] ? 1.0 : 0.0;
432 | }
433 | stptr--;
434 | break;
435 | case RrdRpn.OP_IF:
436 | if(stptr < 2) throw new RrdRpnError();
437 | this.rpnstack[stptr - 2] = (isNaN(this.rpnstack[stptr - 2]) || this.rpnstack[stptr - 2] == 0.0) ? this.rpnstack[stptr] : this.rpnstack[stptr - 1];
438 | stptr--;
439 | stptr--;
440 | break;
441 | case RrdRpn.OP_MIN:
442 | if(stptr < 1) throw new RrdRpnError();
443 | if (isNaN(this.rpnstack[stptr - 1])) {
444 | } else if (isNaN(this.rpnstack[stptr])) {
445 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
446 | } else if (this.rpnstack[stptr - 1] > this.rpnstack[stptr]) {
447 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
448 | }
449 | stptr--;
450 | break;
451 | case RrdRpn.OP_MAX:
452 | if(stptr < 1) throw new RrdRpnError();
453 | if (isNaN(this.rpnstack[stptr - 1])) {
454 | } else if (isNaN(this.rpnstack[stptr])) {
455 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
456 | } else if (this.rpnstack[stptr - 1] < this.rpnstack[stptr]) {
457 | this.rpnstack[stptr - 1] = this.rpnstack[stptr];
458 | }
459 | stptr--;
460 | break;
461 | case RrdRpn.OP_LIMIT:
462 | if(stptr < 2) throw new RrdRpnError();
463 | if (isNaN(this.rpnstack[stptr - 2])) {
464 | } else if (isNaN(this.rpnstack[stptr - 1])) {
465 | this.rpnstack[stptr - 2] = this.rpnstack[stptr - 1];
466 | } else if (isNaN(this.rpnstack[stptr])) {
467 | this.rpnstack[stptr - 2] = this.rpnstack[stptr];
468 | } else if (this.rpnstack[stptr - 2] < this.rpnstack[stptr - 1]) {
469 | this.rpnstack[stptr - 2] = Number.NaN;
470 | } else if (this.rpnstack[stptr - 2] > this.rpnstack[stptr]) {
471 | this.rpnstack[stptr - 2] = Number.NaN;
472 | }
473 | stptr -= 2;
474 | break;
475 | case RrdRpn.OP_UN:
476 | if(stptr < 0) throw new RrdRpnError();
477 | this.rpnstack[stptr] = isNaN(this.rpnstack[stptr]) ? 1.0 : 0.0;
478 | break;
479 | case RrdRpn.OP_ISINF:
480 | if(stptr < 0) throw new RrdRpnError();
481 | this.rpnstack[stptr] = !isFinite(this.rpnstack[stptr]) ? 1.0 : 0.0;
482 | break;
483 | case RrdRpn.OP_SQRT:
484 | if(stptr < 0) throw new RrdRpnError();
485 | this.rpnstack[stptr] = Math.sqrt(this.rpnstack[stptr]);
486 | break;
487 | case RrdRpn.OP_SORT:
488 | if(stptr < 0) throw new RrdRpnError();
489 | var spn = this.rpnstack[stptr--];
490 | if(stptr < spn - 1) throw new RrdRpnError();
491 | var array = this.rpnstack.slice(stptr - spn + 1, stptr +1);
492 | array.sort(this.compare_double);
493 | for (var i=stptr - spn + 1, ii=0; i < (stptr +1) ; i++, ii++)
494 | this.rpnstack[i] = array[ii];
495 | // qsort(this.rpnstack + stptr - spn + 1, spn, sizeof(double), rpn_compare_double);
496 | break;
497 | case RrdRpn.OP_REV:
498 | if(stptr < 0) throw new RrdRpnError();
499 | var spn = this.rpnstack[stptr--];
500 | if(stptr < spn - 1) throw new RrdRpnError();
501 | var array = this.rpnstack.slice(stptr - spn + 1, stptr +1);
502 | array.reverse();
503 | for (var i=stptr - spn + 1, ii=0; i < (stptr +1) ; i++, ii++)
504 | this.rpnstack[i] = array[ii];
505 | break;
506 | case RrdRpn.OP_PREDICT:
507 | case RrdRpn.OP_PREDICTSIGMA:
508 | if(stptr < 2) throw new RrdRpnError();
509 | var locstepsize = this.rpnstack[--stptr];
510 | var shifts = this.rpnstack[--stptr];
511 | if(stptr < shifts) throw new RrdRpnError();
512 | if (shifts<0) stptr--;
513 | else stptr-=shifts;
514 | var val=Number.NaN;
515 | var dsstep = this.rpnp[rpi - 1].step;
516 | var dscount = this.rpnp[rpi - 1].ds_cnt;
517 | var locstep = Math.ceil(locstepsize/dsstep);
518 | var sum = 0;
519 | var sum2 = 0;
520 | var count = 0;
521 | /* now loop for each position */
522 | var doshifts=shifts;
523 | if (shifts<0) doshifts=-shifts;
524 | for(var loop=0;loop=0)&&(offset0) val = sum/count;
547 | } else {
548 | if (count>1) {
549 | val=count*sum2-sum*sum;
550 | if (val<0) {
551 | val=Number.NaN;
552 | } else {
553 | val=Math.sqrt(val/(count*(count-1.0)));
554 | }
555 | }
556 | }
557 | this.rpnstack[stptr] = val;
558 | break;
559 | case RrdRpn.OP_TREND:
560 | case RrdRpn.OP_TRENDNAN:
561 | if(stptr < 1) throw new RrdRpnError();
562 | if ((rpi < 2) || (this.rpnp[rpi - 2].op != RrdRpn.OP_VARIABLE)) {
563 | throw new RrdRpnError("malformed trend arguments");
564 | } else {
565 | var dur = this.rpnstack[stptr];
566 | var step = this.rpnp[rpi - 2].step;
567 |
568 | if (output_idx + 1 >= Math.ceil(dur / step)) {
569 | var ignorenan = (this.rpnp[rpi].op == RrdRpn.OP_TREND);
570 | var accum = 0.0;
571 | var i = 0;
572 | var count = 0;
573 |
574 | do {
575 | var val = this.rpnp[rpi - 2].data[this.rpnp[rpi - 2].ds_cnt * i--];
576 | if (ignorenan || !isNaN(val)) {
577 | accum += val;
578 | ++count;
579 | }
580 | dur -= step;
581 | } while (dur > 0);
582 |
583 | this.rpnstack[--stptr] = (count == 0) ? Number.NaN : (accum / count);
584 | } else this.rpnstack[--stptr] = Number.NaN;
585 | }
586 | break;
587 | case RrdRpn.OP_AVG:
588 | if(stptr < 0) throw new RrdRpnError();
589 | var i = this.rpnstack[stptr--];
590 | var sum = 0;
591 | var count = 0;
592 |
593 | if(stptr < i - 1) throw new RrdRpnError();
594 | while (i > 0) {
595 | var val = this.rpnstack[stptr--];
596 | i--;
597 | if (isNaN(val)) continue;
598 | count++;
599 | sum += val;
600 | }
601 | if (count > 0) this.rpnstack[++stptr] = sum / count;
602 | else this.rpnstack[++stptr] = Number.NaN;
603 | break;
604 | case RrdRpn.OP_ABS:
605 | if(stptr < 0) throw new RrdRpnError();
606 | this.rpnstack[stptr] = Math.abs(this.rpnstack[stptr]);
607 | break;
608 | case RrdRpn.OP_END:
609 | break;
610 | }
611 | }
612 | if (stptr != 0) throw new RrdRpnError("RPN final stack size != 1");
613 | output[output_idx] = this.rpnstack[0];
614 | return 0;
615 | };
616 |
617 |
--------------------------------------------------------------------------------
/js/RrdJson.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This program is free software; you can redistribute it and/or modify it
4 | * under the terms of the GNU General Public License as published by the Free
5 | * Software Foundation; either version 2 of the License, or (at your option)
6 | * any later version.
7 | *
8 | * This program is distributed in the hope that it will be useful, but WITHOUT
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 | * more details.
12 |
13 | * You should have received a copy of the GNU General Public License along
14 | * with this program; if not, write to the Free Software Foundation, Inc.,
15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16 |
17 | *
18 | * Manuel Sanmartin
19 | **/
20 |
21 | "use strict";
22 |
23 | /**
24 | * RrdJson
25 | * @constructor
26 | */
27 | var RrdJson = function() {
28 | if (arguments.length == 1) {
29 | this.init1.apply(this, arguments);
30 | } else if (arguments.length == 2) {
31 | this.init2.apply(this, arguments);
32 | } else if (arguments.length == 3) {
33 | this.init3.apply(this, arguments);
34 | }
35 | };
36 |
37 | RrdJson.prototype = {
38 | graph: null,
39 | json: null,
40 |
41 | init1: function (rrdgraph)
42 | {
43 | this.graph = rrdgraph;
44 | },
45 | init2: function (rrdgraph, jsonstr)
46 | {
47 | this.json = JSON.parse(jsonstr);
48 | this.graph = rrdgraph;
49 | },
50 | init3: function (gfx, fetch, jsonstr)
51 | {
52 | this.json = JSON.parse(jsonstr);
53 | this.graph = new RrdGraph(gfx, fetch);
54 | },
55 | parse: function()
56 | {
57 | for (var option in this.json) {
58 | switch(option) {
59 | case 'alt_autoscale':
60 | this.graph.alt_autoscale = this.json.alt_autoscale;
61 | break;
62 | case 'base':
63 | this.graph.base = parseInt(this.json.base, 10);
64 | if (this.graph.base !== 1000 && this.graph.base !== 1024)
65 | throw 'the only sensible value for base apart from 1000 is 1024';
66 | break;
67 | case 'color':
68 | for (var color in this.json.color) {
69 | if (color in this.graph.GRC) {
70 | this.graph.GRC[color] = this.json.color[color];
71 | } else {
72 | throw "invalid color '" + color + "'";
73 | }
74 | }
75 | break;
76 | case 'full_size_mode':
77 | this.graph.full_size_mode = this.json.full_size_mode;
78 | break;
79 | case 'slope_mode':
80 | this.graph.slopemode = this.json.slope_mode;
81 | break;
82 | case 'end':
83 | this.graph.end_t = new RrdTime(this.json.end);
84 | break;
85 | case 'force_rules_legend':
86 | this.graph.force_rules_legend = this.json.force_rules_legend;
87 | break;
88 | case 'no_legend':
89 | this.graph.no_legend = this.json.no_legend;
90 | break;
91 | case 'height':
92 | this.graph.ysize = this.json.height;
93 | break;
94 | case 'no_minor':
95 | this.graph.no_minor = this.json.no_minor;
96 | break;
97 | case 'alt_autoscale_min':
98 | this.graph.alt_autoscale_min = this.json.alt_autoscale_min;
99 | break;
100 | case 'only_graph':
101 | this.graph.only_graph = this.json.only_graph;
102 | break;
103 | case 'units_length':
104 | this.graph.unitslength = this.json.units_length; // FIXME
105 | this.graph.forceleftspace = true;
106 | break;
107 | case 'lower_limit':
108 | if (this.json.lower_limit === null) this.graph.setminval = Number.NaN;
109 | else this.graph.setminval = this.json.lower_limit;
110 | break;
111 | case 'alt_autoscale_max':
112 | this.graph.alt_autoscale_max = this.json.alt_autoscale_max;
113 | break;
114 | case 'zoom':
115 | this.graph.zoom = this.json.zoom;
116 | if (this.graph.zoom <= 0.0)
117 | throw "zoom factor must be > 0";
118 | break;
119 | case 'no_gridfit':
120 | this.graph.gridfit = this.json.no_gridfit;
121 | break;
122 | case 'font':
123 | for (var font in this.json.font) {
124 | if (font in this.graph.TEXT) {
125 | if (this.json.font[font].size !== undefined)
126 | this.graph.TEXT[font].size = this.json.font[font].size;
127 | if (this.json.font[font].font !== undefined)
128 | this.graph.TEXT[font].font = this.json.font[font].font;
129 | } else {
130 | throw "invalid text property name";
131 | }
132 | }
133 | break;
134 | case 'logarithmic':
135 | this.graph.logarithmic = this.json.logarithmic;
136 | break;
137 | case 'rigid':
138 | this.graph.rigid = this.json.rigid;
139 | break;
140 | case 'step':
141 | this.graph.step = this.json.step;
142 | this.graph.step_orig = this.json.step;
143 | break;
144 | case 'start':
145 | this.graph.start_t = new RrdTime(this.json.start);
146 | break;
147 | case 'tabwidth':
148 | this.graph.tabwidth = this.json.tabwidth;
149 | break;
150 | case 'title':
151 | this.graph.title = this.json.title;
152 | break;
153 | case 'upper_limit':
154 | if (this.json.upper_limit === null) this.graph.setmaxval = Number.NaN;
155 | else this.graph.setmaxval = this.json.upper_limit;
156 | break;
157 | case 'vertical_label':
158 | this.graph.ylegend = this.json.vertical_label;
159 | break;
160 | case 'watermark':
161 | this.graph.watermark = this.json.watermark;
162 | break;
163 | case 'width':
164 | this.graph.xsize = this.json.width;
165 | if (this.graph.xsize < 10)
166 | throw "width below 10 pixels";
167 | break;
168 | case 'units_exponent':
169 | this.graph.unitsexponent = this.json.units_exponent;
170 | break;
171 | case 'x_grid':
172 | break;
173 | case 'alt_ygrid':
174 | this.graph.alt_ygrid = this.json.alt_ygrid;
175 | break;
176 | case 'y_grid':
177 | break;
178 | case 'lazy':
179 | this.graph.lazy = this.json.lazy;
180 | break;
181 | case 'units':
182 | break;
183 | case 'disable_rrdtool_tag':
184 | this.graph.no_rrdtool_tag = this.json.disable_rrdtool_tag;
185 | break;
186 | case 'right_axis':
187 | break;
188 | case 'right_axis_label':
189 | this.graph.second_axis_legend = this.json.right_axis_label;
190 | break;
191 | case 'right_axis_format':
192 | this.graph.second_axis_format = this.json.right_axis_format;
193 | break;
194 | case 'legend_position':
195 | switch (this.json.legend_position) {
196 | case "north":
197 | this.graph.legendposition = this.graph.LEGEND_POS.NORTH;
198 | break;
199 | case "west":
200 | this.graph.legendposition = this.graph.LEGEND_POS.WEST;
201 | break;
202 | case "south":
203 | this.graph.legendposition = this.graph.LEGEND_POS.SOUTH;
204 | break;
205 | case "east":
206 | this.graph.legendposition = this.graph.LEGEND_POS.EAST;
207 | break;
208 | default:
209 | throw "unknown legend-position '" + this.json.legend_position + "'";
210 | }
211 | break;
212 | case 'legend_direction':
213 | if (this.json.legend_direction === "topdown") {
214 | this.graph.legenddirection = this.graph.LEGEND_DIR.TOP_DOWN;
215 | } else if (this.json.legend_direction === "bottomup") {
216 | this.graph.legenddirection = this.graph.LEGEND_DIR.BOTTOM_UP;
217 | } else {
218 | throw "unknown legend-direction'" + this.json.legend_direction + "'";
219 | }
220 | break;
221 | case 'border':
222 | this.graph.draw_3d_border = this.json.border;
223 | break;
224 | case 'grid_dash':
225 | if (this.json.grid_dash.length !== 2)
226 | throw "expected grid-dash format float:float";
227 | this.graph.grid_dash_on = this.json.grid_dash[0];
228 | this.graph.grid_dash_off = this.json.grid_dash[1];
229 | break;
230 | case 'dynamic_labels':
231 | this.graph.dynamic_labels = this.json.dynamic_labels;
232 | break;
233 | case 'gdes':
234 | this.parse_gdes(this.json.gdes);
235 | break;
236 | default:
237 | throw 'Unknow option "'+option+'"';
238 | }
239 | }
240 | var start_end = RrdTime.proc_start_end(this.graph.start_t, this.graph.end_t); // FIXME here?
241 | this.graph.start = start_end[0];
242 | this.graph.end = start_end[1];
243 | },
244 | parse_gdes: function (gdes)
245 | {
246 | for (var i = 0, gdes_c = gdes.length; i < gdes_c; i++) {
247 | switch (gdes[i].type) {
248 | // GPRINT:vname:format
249 | case 'GPRINT':
250 | this.graph.gdes_add_gprint(gdes[i].vname, gdes[i].cf, gdes[i].format, gdes[i].strftm);
251 | break;
252 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]
253 | case 'LINE':
254 | this.graph.gdes_add_line(gdes[i].width, gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack, gdes[i].dashes, gdes[i].dash_offset);
255 | break;
256 | // AREA:value[#color][:[legend][:STACK]]
257 | case 'AREA':
258 | this.graph.gdes_add_area(gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack);
259 | break;
260 | // TICK:vname#rrggbb[aa][:fraction[:legend]]
261 | case 'TICK':
262 | this.graph.gdes_add_tick(gdes[i].vname, gdes[i].color, gdes[i].fraction, gdes[i].legend);
263 | break;
264 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]
265 | case 'HRULE':
266 | this.graph.gdes_add_hrule(gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].dashes, gdes[i].dash_offset);
267 | break;
268 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]
269 | case 'VRULE':
270 | this.graph.gdes_add_vrule(gdes[i].time, gdes[i].color, gdes[i].legend, gdes[i].dashes, gdes[i].dash_offset);
271 | break;
272 | // COMMENT:text
273 | case 'COMMENT':
274 | this.graph.gdes_add_comment(gdes[i].legend);
275 | break;
276 | // TEXTALIGN:{left|right|justified|center}
277 | case 'TEXTALIGN':
278 | switch (gdes[i].align) {
279 | case 'left':
280 | this.graph.gdes_add_textalign(RrdGraphDesc.TXA_LEFT);
281 | break;
282 | case 'right':
283 | this.graph.gdes_add_textalign(RrdGraphDesc.TXA_RIGHT);
284 | break;
285 | case 'justified':
286 | this.graph.gdes_add_textalign(RrdGraphDesc.TXA_JUSTIFIED);
287 | break;
288 | case 'center':
289 | this.graph.gdes_add_textalign(RrdGraphDesc.TXA_CENTER);
290 | break;
291 | }
292 | break;
293 | // DEF:=::[:step=][:start=