diff options
author | Pim van den Berg | 2013-05-10 21:36:54 +0200 |
---|---|---|
committer | Pim van den Berg | 2013-05-10 21:36:54 +0200 |
commit | 0163faefef02207ad0ea3330af688103633293ff (patch) | |
tree | c407fca93a3418af59c4c7133327f5bc1630fba5 | |
parent | Merge pull request #5 from mce35/nut (diff) | |
download | apt-panopticon_cgp-0163faefef02207ad0ea3330af688103633293ff.zip apt-panopticon_cgp-0163faefef02207ad0ea3330af688103633293ff.tar.gz apt-panopticon_cgp-0163faefef02207ad0ea3330af688103633293ff.tar.bz2 apt-panopticon_cgp-0163faefef02207ad0ea3330af688103633293ff.tar.xz |
import js directory from jsrrdgraph for client side graph rendering
Source: https://github.com/manuelluis/jsrrdgraph@276b880
-rw-r--r-- | js/Color.js | 76 | ||||
-rw-r--r-- | js/RrdCmdLine.js | 513 | ||||
-rw-r--r-- | js/RrdDataFile.js | 129 | ||||
-rw-r--r-- | js/RrdGfxCanvas.js | 254 | ||||
-rw-r--r-- | js/RrdGfxPdf.js | 1014 | ||||
-rw-r--r-- | js/RrdGfxSvg.js | 250 | ||||
-rw-r--r-- | js/RrdGraph.js | 2914 | ||||
-rw-r--r-- | js/RrdJson.js | 588 | ||||
-rw-r--r-- | js/RrdRpn.js | 616 | ||||
-rw-r--r-- | js/RrdTime.js | 621 | ||||
-rw-r--r-- | js/base64.js | 143 | ||||
-rw-r--r-- | js/binaryXHR.js | 234 | ||||
-rw-r--r-- | js/rrdFile.js | 408 | ||||
-rw-r--r-- | js/sprintf.js | 78 | ||||
-rw-r--r-- | js/strftime.js | 146 |
15 files changed, 7984 insertions, 0 deletions
diff --git a/js/Color.js b/js/Color.js new file mode 100644 index 0000000..e294f65 --- /dev/null +++ b/js/Color.js | |||
@@ -0,0 +1,76 @@ | |||
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.prototype = Error.prototype; | ||
28 | this.name = "ColorError"; | ||
29 | this.message = (message) ? message : "Error"; | ||
30 | }; | ||
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 | |||
diff --git a/js/RrdCmdLine.js b/js/RrdCmdLine.js new file mode 100644 index 0000000..fea0753 --- /dev/null +++ b/js/RrdCmdLine.js | |||
@@ -0,0 +1,513 @@ | |||
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 <manuel.luis at gmail.com> | ||
19 | **/ | ||
20 | |||
21 | "use strict"; | ||
22 | |||
23 | /** | ||
24 | * RrdCmdLine | ||
25 | * @constructor | ||
26 | */ | ||
27 | var RrdCmdLine = function() { | ||
28 | // if (arguments.lenght === 3) // XXX | ||
29 | this.init.apply(this, arguments); | ||
30 | }; | ||
31 | |||
32 | RrdCmdLine.prototype = { | ||
33 | graph: null, | ||
34 | |||
35 | init: function (gfx, fetch, line) | ||
36 | { | ||
37 | this.graph = new RrdGraph(gfx, fetch); | ||
38 | this.cmdline(line); | ||
39 | }, | ||
40 | cmdline: function(line) // FIXME | ||
41 | { | ||
42 | var i = 0; | ||
43 | line = line.replace(/\n/g," "); | ||
44 | var lines = line.match(/[^" ]+|"[^"]+"/g); | ||
45 | var len = lines.length; | ||
46 | |||
47 | while (i < len) { | ||
48 | var arg = lines[i]; | ||
49 | if (arg.charAt(0) === '"' && arg.charAt(arg.length-1) === '"') | ||
50 | arg = arg.substr(1,arg.length-2); | ||
51 | if (/^LINE[0-9.]+:/.test(arg)) { | ||
52 | this.parse_line(arg); | ||
53 | } else if (/^AREA:/.test(arg)) { | ||
54 | this.parse_area(arg); | ||
55 | } else if (/^DEF:/.test(arg)) { | ||
56 | this.parse_def(arg); | ||
57 | } else if (/^CDEF:/.test(arg)) { | ||
58 | this.parse_cdef(arg); | ||
59 | } else if (/^VDEF:/.test(arg)) { | ||
60 | this.parse_vdef(arg); | ||
61 | } else if (/^GPRINT:/.test(arg)) { | ||
62 | this.parse_gprint(arg); | ||
63 | } else if (/^COMMENT:/.test(arg)) { | ||
64 | this.parse_comment(arg); | ||
65 | } else if (/^VRULE:/.test(arg)) { | ||
66 | this.parse_vrule(arg); | ||
67 | } else if (/^HRULE:/.test(arg)) { | ||
68 | this.parse_hrule(arg); | ||
69 | } else if (/^TICK:/.test(arg)) { | ||
70 | this.parse_tick(arg); | ||
71 | } else if (/^TEXTALIGN:/.test(arg)) { | ||
72 | this.parse_textaling(arg); | ||
73 | } else if (/^SHIFT:/.test(arg)) { | ||
74 | this.parse_shift(arg); | ||
75 | } else if (arg.charAt(0) === '-') { | ||
76 | var strip = 1; | ||
77 | if (arg.length > 1 && arg.charAt(1) === '-') { | ||
78 | strip = 2; | ||
79 | } | ||
80 | var option = arg.substr(strip); | ||
81 | var value = undefined; | ||
82 | |||
83 | if (option.indexOf('=') !== -1) { | ||
84 | var index = option.indexOf('='); | ||
85 | value = option.substr(index+1); | ||
86 | option = option.substr(0,index); | ||
87 | } else if (i+1 < len) { | ||
88 | if (lines[i+1].charAt(0) !== '-' && | ||
89 | !/^"?LINE[0-9.]+:/.test(lines[i+1]) && | ||
90 | !/^"?AREA:/.test(lines[i+1]) && | ||
91 | !/^"?DEF:/.test(lines[i+1]) && | ||
92 | !/^"?CDEF:/.test(lines[i+1]) && | ||
93 | !/^"?VDEF:/.test(lines[i+1]) && | ||
94 | !/^"?GPRINT:/.test(lines[i+1]) && | ||
95 | !/^"?COMMENT:/.test(lines[i+1]) && | ||
96 | !/^"?HRULE:/.test(lines[i+1]) && | ||
97 | !/^"?VRULE:/.test(lines[i+1]) && | ||
98 | !/^"?TICK:/.test(lines[i+1]) && | ||
99 | !/^"?TEXTALING:/.test(lines[i+1]) && | ||
100 | !/^"?SHIFT:/.test(lines[i+1]) | ||
101 | ) { | ||
102 | i++; | ||
103 | if (lines[i].charAt(0) === '"' && lines[i].charAt(lines[i].length-1) === '"') | ||
104 | value = lines[i].substr(1,lines[i].length-2); | ||
105 | else | ||
106 | value = lines[i]; | ||
107 | } | ||
108 | } | ||
109 | this.set_option(option, value); | ||
110 | } else { | ||
111 | throw "Unknow argument: "+arg; | ||
112 | } | ||
113 | i++; | ||
114 | } | ||
115 | var start_end = RrdTime.proc_start_end(this.graph.start_t, this.graph.end_t); // FIXME here? | ||
116 | this.graph.start = start_end[0]; | ||
117 | this.graph.end = start_end[1]; | ||
118 | }, | ||
119 | set_option: function(option, value) | ||
120 | { | ||
121 | switch(option) { | ||
122 | case 'alt-autoscale': | ||
123 | case 'A': | ||
124 | this.graph.alt_autoscale = true; | ||
125 | break; | ||
126 | case 'base': | ||
127 | case 'b': | ||
128 | this.graph.base = parseInt(value, 10); | ||
129 | if (this.graph.base !== 1000 && this.graph.base !== 1024) | ||
130 | throw 'the only sensible value for base apart from 1000 is 1024'; | ||
131 | break; | ||
132 | case 'color': | ||
133 | case 'c': | ||
134 | var index = value.indexOf('#'); | ||
135 | if (index === -1) | ||
136 | throw "invalid color def format"; | ||
137 | var name = value.substr(0,index); | ||
138 | if (!this.graph.GRC[name]) | ||
139 | throw "invalid color name '"+name+"'" | ||
140 | this.graph.GRC[name] = value.substr(index); // FIXME check color | ||
141 | break; | ||
142 | case 'full-size-mode': | ||
143 | case 'D': | ||
144 | this.graph.full_size_mode = true; | ||
145 | break; | ||
146 | case 'slope-mode': | ||
147 | case 'E': | ||
148 | this.graph.slopemode = true; | ||
149 | break; | ||
150 | case 'end': | ||
151 | case 'e': | ||
152 | this.graph.end_t = new RrdTime(value); | ||
153 | // this.graph.end = parseInt(value, 10); | ||
154 | break; | ||
155 | case 'force-rules-legend': | ||
156 | case 'F': | ||
157 | this.graph.force_rules_legend = true; | ||
158 | break; | ||
159 | case 'imginfo': | ||
160 | case 'f': | ||
161 | // im->imginfo = optarg; | ||
162 | break; | ||
163 | case 'graph-render-mode': | ||
164 | case 'G': | ||
165 | // im->graph_antialias | ||
166 | break; | ||
167 | case 'no-legend': | ||
168 | case 'g': | ||
169 | this.graph.no_legend = true; | ||
170 | break; | ||
171 | case 'height': | ||
172 | case 'h': | ||
173 | this.graph.ysize = parseInt(value, 10); | ||
174 | break; | ||
175 | case 'no-minor': | ||
176 | case 'I': | ||
177 | this.graph.no_minor = false; | ||
178 | break; | ||
179 | case 'interlaced': | ||
180 | case 'i': | ||
181 | break; | ||
182 | case 'alt-autoscale-min': | ||
183 | case 'J': | ||
184 | this.graph.alt_autoscale_min = true; | ||
185 | break; | ||
186 | case 'only-graph': | ||
187 | case 'j': | ||
188 | this.graph.only_graph = true; | ||
189 | break; | ||
190 | case 'units-length': | ||
191 | case 'L': | ||
192 | this.graph.unitslength = parseInt(value, 10); | ||
193 | this.graph.forceleftspace = true; | ||
194 | break; | ||
195 | case 'lower-limit': | ||
196 | case 'l': | ||
197 | this.graph.setminval = parseFloat(value) | ||
198 | break; | ||
199 | case 'alt-autoscale-max': | ||
200 | case 'M': | ||
201 | this.graph.alt_autoscale_max = true; | ||
202 | break; | ||
203 | case 'zoom': | ||
204 | case 'm': | ||
205 | this.graph.zoom = parseFloat(value); | ||
206 | if (this.graph.zoom <= 0.0) | ||
207 | throw "zoom factor must be > 0"; | ||
208 | break; | ||
209 | case 'no-gridfit': | ||
210 | case 'N': | ||
211 | this.graph.gridfit = true; | ||
212 | break; | ||
213 | case 'font': | ||
214 | case 'n': | ||
215 | var args = value.split(':'); | ||
216 | if (args.length !== 3) | ||
217 | throw "invalid text property format"; | ||
218 | if (!this.graph.TEXT[args[0]]) | ||
219 | throw "invalid fonttag '"+args[0]+"'" | ||
220 | if (args[1] > 0) | ||
221 | this.graph.TEXT[args[0]].size = args[1]; | ||
222 | if (args[2]) | ||
223 | this.graph.TEXT[args[0]].font = args[2]; | ||
224 | break; | ||
225 | case 'logarithmic': | ||
226 | case 'o': | ||
227 | this.graph.logarithmic = true; | ||
228 | break; | ||
229 | case 'pango-markup': | ||
230 | case 'P': | ||
231 | // im->with_markup = 1; | ||
232 | break; | ||
233 | case 'font-render-mode': | ||
234 | case 'R': | ||
235 | // im->font_options: normal light mono | ||
236 | break; | ||
237 | case 'rigid': | ||
238 | case 'r': | ||
239 | this.graph.rigid = true; | ||
240 | break; | ||
241 | case 'step': | ||
242 | this.graph.step = parseInt(value, 10); | ||
243 | break; | ||
244 | case 'start': | ||
245 | case 's': | ||
246 | this.graph.start_t = new RrdTime(value); | ||
247 | //this.graph.start = parseInt(value, 10); | ||
248 | break; | ||
249 | case 'tabwidth': | ||
250 | case 'T': | ||
251 | this.graph.tabwidth = parseFloat(value); | ||
252 | break; | ||
253 | case 'title': | ||
254 | case 't': | ||
255 | this.graph.title = value; | ||
256 | break; | ||
257 | case 'upper-limit': | ||
258 | case 'u': | ||
259 | this.graph.setmaxval = parseFloat(value); | ||
260 | break; | ||
261 | case 'vertical-label': | ||
262 | case 'v': | ||
263 | this.graph.ylegend = value; | ||
264 | break; | ||
265 | case 'watermark': | ||
266 | case 'W': | ||
267 | this.graph.watermark = value; | ||
268 | break; | ||
269 | case 'width': | ||
270 | case 'w': | ||
271 | this.graph.xsize = parseInt(value, 10); | ||
272 | if (this.graph.xsize < 10) | ||
273 | throw "width below 10 pixels"; | ||
274 | break; | ||
275 | case 'units-exponent': | ||
276 | case 'X': | ||
277 | this.graph.unitsexponent = parseInt(value, 10); | ||
278 | break; | ||
279 | case 'x-grid': | ||
280 | case 'x': | ||
281 | if (value === 'none') { | ||
282 | this.graph.draw_x_grid = false; | ||
283 | } else { | ||
284 | var args = value.split(':'); | ||
285 | if (args.length !== 8) | ||
286 | throw "invalid x-grid format"; | ||
287 | this.graph.xlab_user.gridtm = this.graph.tmt_conv(args[0]); | ||
288 | if (this.graph.xlab_user.gridtm < 0) | ||
289 | throw "unknown keyword "+args[0]; | ||
290 | this.graph.xlab_user.gridst = parseInt(args[1], 10); | ||
291 | this.graph.xlab_user.mgridtm = this.graph.tmt_conv(args[2]); | ||
292 | if (this.graph.xlab_user.mgridtm < 2) | ||
293 | throw "unknown keyword "+args[2]; | ||
294 | this.graph.xlab_user.mgridst = parseInt(args[3], 10); | ||
295 | this.graph.xlab_user.labtm = this.graph.tmt_conv(args[4]); | ||
296 | if (this.graph.xlab_user.labtm < 0) | ||
297 | throw "unknown keyword "+args[4]; | ||
298 | this.graph.xlab_user.labst = parseInt(args[5], 10); | ||
299 | this.graph.xlab_user.precis = parseInt(args[6], 10); | ||
300 | this.graph.xlab_user.minsec = 1; | ||
301 | this.graph.xlab_form = args[7]; // FIXME : ? join(:) | ||
302 | this.graph.xlab_user.stst = this.graph.xlab_form; | ||
303 | } | ||
304 | break; | ||
305 | case 'alt-y-grid': | ||
306 | case 'Y': | ||
307 | this.graph.alt_ygrid = true; | ||
308 | break; | ||
309 | case 'y-grid': | ||
310 | case 'y': | ||
311 | if (value === 'none') { | ||
312 | this.graph.draw_y_grid = false; | ||
313 | } else { | ||
314 | var index = value.indexOf(':'); | ||
315 | if (index === -1) | ||
316 | throw "invalid y-grid format"; | ||
317 | this.graph.ygridstep = parseFloat(value.substr(0,index)); | ||
318 | if (this.graph.ygridstep <= 0) | ||
319 | throw "grid step must be > 0"; | ||
320 | this.graph.ylabfact = parseInt(value.substr(index+1), 10); | ||
321 | if (this.graph.ylabfact < 1) | ||
322 | throw "label factor must be > 0"; | ||
323 | } | ||
324 | break; | ||
325 | case 'lazy': | ||
326 | case 'z': | ||
327 | this.graph.lazy = 1; | ||
328 | break; | ||
329 | case 'units': | ||
330 | if (this.graph.force_units) | ||
331 | throw "--units can only be used once!"; | ||
332 | if (value === 'si') | ||
333 | this.graph.force_units_si = true; | ||
334 | else | ||
335 | throw "invalid argument for --units: "+value; | ||
336 | break; | ||
337 | case 'alt-y-mrtg': | ||
338 | break; | ||
339 | case 'disable-rrdtool-tag': | ||
340 | this.graph.no_rrdtool_tag = true; | ||
341 | break; | ||
342 | case 'right-axis': | ||
343 | var index = value.indexOf(':'); | ||
344 | if (index === -1) | ||
345 | throw "invalid right-axis format expected scale:shift"; | ||
346 | this.graph.second_axis_scale = parseFloat(value.substr(0,index)); | ||
347 | if(this.graph.second_axis_scale === 0) | ||
348 | throw "the second_axis_scale must not be 0"; | ||
349 | this.graph.second_axis_shift = parseFloat(value.substr(index+1)); | ||
350 | break; | ||
351 | case 'right-axis-label': | ||
352 | this.graph.second_axis_legend = value; | ||
353 | break; | ||
354 | case 'right-axis-format': | ||
355 | this.graph.second_axis_format = value; | ||
356 | break; | ||
357 | case 'legend-position': | ||
358 | if (value === "north") { | ||
359 | this.graph.legendposition = this.graph.LEGEND_POS.NORTH; | ||
360 | } else if (value === "west") { | ||
361 | this.graph.legendposition = this.graph.LEGEND_POS.WEST; | ||
362 | } else if (value === "south") { | ||
363 | this.graph.legendposition = this.graph.LEGEND_POS.SOUTH; | ||
364 | } else if (value === "east") { | ||
365 | this.graph.legendposition = this.graph.LEGEND_POS.EAST; | ||
366 | } else { | ||
367 | throw "unknown legend-position '"+value+"'"; | ||
368 | } | ||
369 | break; | ||
370 | case 'legend-direction': | ||
371 | if (value === "topdown") { | ||
372 | this.graph.legenddirection = this.graph.LEGEND_DIR.TOP_DOWN; | ||
373 | } else if (value === "bottomup") { | ||
374 | this.graph.legenddirection = this.graph.LEGEND_DIR.BOTTOM_UP; | ||
375 | } else { | ||
376 | throw "unknown legend-position '"+value+"'"; | ||
377 | } | ||
378 | break; | ||
379 | case 'border': | ||
380 | this.graph.draw_3d_border = parseInt(value, 10); | ||
381 | break; | ||
382 | case 'grid-dash': | ||
383 | var index = value.indexOf(':'); | ||
384 | if (index === -1) | ||
385 | throw "expected grid-dash format float:float"; | ||
386 | this.graph.grid_dash_on = parseFloat(value.substr(0,index)); | ||
387 | this.graph.grid_dash_off = parseFloat(value.substr(index+1)); | ||
388 | break; | ||
389 | case 'dynamic-labels': | ||
390 | this.graph.dynamic_labels = true; | ||
391 | break; | ||
392 | default: | ||
393 | throw 'Unknow option "'+option+'"'; | ||
394 | } | ||
395 | |||
396 | }, | ||
397 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
398 | parse_def: function (line) | ||
399 | { | ||
400 | var args = line.split(/:/); | ||
401 | var n=1; | ||
402 | var vnames = args[n++].split('='); | ||
403 | var vname = vnames[0]; | ||
404 | var rrdfile = vnames[1]; | ||
405 | var name = args[n++]; | ||
406 | var cf = args[n++]; | ||
407 | var step = undefined; | ||
408 | var reduce = undefined; | ||
409 | var start = undefined; | ||
410 | var end = undefined; | ||
411 | if (args.length > n) { | ||
412 | for (var j = n, xlen = args.length ; j < xlen ; j++) { | ||
413 | var opts = args[j].split("="); | ||
414 | if (opts[0] === "step") step = opts[1]; | ||
415 | if (opts[0] === "reduce") reduce = opts[1] | ||
416 | if (opts[0] === "start") start = opts[1]; | ||
417 | if (opts[0] === "end") end = opts[1]; | ||
418 | } | ||
419 | } | ||
420 | this.graph.gdes_add_def(vname, rrdfile, name, cf, step, start, end, reduce) | ||
421 | }, | ||
422 | // CDEF:vname=RPN expression | ||
423 | parse_cdef: function (line) | ||
424 | { | ||
425 | var args = line.split(/:|=/); | ||
426 | this.graph.gdes_add_cdef(args[1], args[2]); | ||
427 | }, | ||
428 | // VDEF:vname=RPN expression | ||
429 | parse_vdef: function (line) | ||
430 | { | ||
431 | var args = line.split(/:|=/); | ||
432 | this.graph.gdes_add_vdef(args[1], args[2]); | ||
433 | }, | ||
434 | // SHIFT:vname:offset | ||
435 | parse_shift: function (line) | ||
436 | { | ||
437 | var args = line.split(':'); | ||
438 | this.graph.gdes_add_shift(args[1], args[2]); | ||
439 | }, | ||
440 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
441 | parse_line: function (line) | ||
442 | { | ||
443 | var args = line.split(/#|:/); | ||
444 | var width = parseFloat(args[0].substr(4)); | ||
445 | var stack = args[4] === 'STACK' ? true : undefined; | ||
446 | var color = this.graph.parse_color(args[2]); | ||
447 | this.graph.gdes_add_line(width, args[1], this.graph.color2rgba(color), args[3], stack); | ||
448 | }, | ||
449 | // AREA:value[#color][:[legend][:STACK]] | ||
450 | parse_area: function (line) | ||
451 | { | ||
452 | var args = line.split(/#|:/); | ||
453 | var stack = args[3] === 'STACK' ? true : undefined; | ||
454 | var color = this.graph.parse_color(args[2]); | ||
455 | this.graph.gdes_add_area(args[1], this.graph.color2rgba(color), stack); | ||
456 | }, | ||
457 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
458 | parse_tick: function (line) | ||
459 | { | ||
460 | var args = line.split(/:|#/); | ||
461 | var color = this.graph.parse_color(args[2]); | ||
462 | this.graph.gdes_add_tick(args[1], this.graph.color2rgba(color), args[3], args[4]); | ||
463 | }, | ||
464 | // GPRINT:vname:format | ||
465 | parse_gprint: function(line) | ||
466 | { | ||
467 | var args = line.split(':'); | ||
468 | var strftime = false; | ||
469 | var vname = args[1]; | ||
470 | var cf = args[2]; | ||
471 | var format = ""; | ||
472 | if (args.length > 3) { | ||
473 | var m=0; | ||
474 | for (var j = 3, xlen = args.length ; j < xlen ; j++) { | ||
475 | if (args[j] === 'strftime') { | ||
476 | strftime = true; | ||
477 | } else { | ||
478 | if (m>0) { | ||
479 | format = format + ':'+ args[j]; | ||
480 | } else { | ||
481 | format = args[j]; | ||
482 | } | ||
483 | m++; | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | this.graph.gdes_add_gprint(vname, cf, format, strftime); | ||
488 | }, | ||
489 | //COMMENT:text | ||
490 | parse_comment: function (line) | ||
491 | { | ||
492 | var index = line.indexOf(':'); | ||
493 | this.graph.gdes_add_comment(line.substr(index+1)); | ||
494 | }, | ||
495 | // TEXTALIGN:{left|right|justified|center} | ||
496 | parse_textaling: function (line) | ||
497 | { | ||
498 | var index = line.indexOf(':'); | ||
499 | this.graph.gdes_add_textaling(line.substr(index+1)); | ||
500 | }, | ||
501 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
502 | parse_vrule: function (line) | ||
503 | { | ||
504 | var args = line.split(/:|#/); | ||
505 | this.graph.gdes_add_vrule(args[1], '#'+args[2], args[3]); | ||
506 | }, | ||
507 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
508 | parse_hrule: function (line) | ||
509 | { | ||
510 | var args = line.split(/:|#/); | ||
511 | this.graph.gdes_add_hrule(args[1], '#'+args[2], args[3]); | ||
512 | } | ||
513 | }; | ||
diff --git a/js/RrdDataFile.js b/js/RrdDataFile.js new file mode 100644 index 0000000..ae4dcff --- /dev/null +++ b/js/RrdDataFile.js | |||
@@ -0,0 +1,129 @@ | |||
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 <manuel.luis at gmail.com> | ||
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 | }, | ||
38 | fetch: function(gdp, ft_step) | ||
39 | { | ||
40 | var cal_start, cal_end; | ||
41 | var best_full_rra = 0, best_part_rra = 0, chosen_rra = 0; | ||
42 | var best_full_step_diff = 0, best_part_step_diff = 0, tmp_step_diff = 0, tmp_match = 0, best_match = 0; | ||
43 | var full_match, rra_base; | ||
44 | var first_full = 1; | ||
45 | var first_part = 1; | ||
46 | var rrd; | ||
47 | var data_ptr; | ||
48 | var rows; | ||
49 | |||
50 | if (gdp.rrd in this.rrdfiles) { | ||
51 | rrd = this.rrdfiles[gdp.rrd]; | ||
52 | } else { | ||
53 | var bf = FetchBinaryURL(gdp.rrd); | ||
54 | rrd = new RRDFile(bf); | ||
55 | this.rrdfiles[gdp.rrd] = rrd; | ||
56 | } | ||
57 | |||
58 | var cf_idx = gdp.cf; | ||
59 | var ds_cnt = rrd.getNrDSs(); | ||
60 | var rra_cnt = rrd.getNrRRAs(); | ||
61 | |||
62 | for (var i = 0; i < ds_cnt; i++) | ||
63 | gdp.ds_namv[i] = rrd.rrd_header.getDSbyIdx(i).getName(); | ||
64 | |||
65 | for (var i = 0; i < rra_cnt; i++) { | ||
66 | var rra = rrd.getRRAInfo(i); | ||
67 | if (RrdGraphDesc.cf_conv(rra.getCFName()) === cf_idx) { | ||
68 | cal_end = (rrd.getLastUpdate() - (rrd.getLastUpdate() % (rra.getPdpPerRow() * rra.pdp_step))); | ||
69 | cal_start = (cal_end - (rra.getPdpPerRow() * rra.row_cnt * rra.pdp_step)); | ||
70 | full_match = gdp.end - gdp.start; | ||
71 | |||
72 | tmp_step_diff = Math.abs(ft_step - (rrd.getMinStep() * rra.pdp_cnt)); | ||
73 | if (cal_start <= gdp.start) { | ||
74 | if (first_full || (tmp_step_diff < best_full_step_diff)) { | ||
75 | first_full = 0; | ||
76 | best_full_step_diff = tmp_step_diff; | ||
77 | best_full_rra = i; | ||
78 | } | ||
79 | } else { | ||
80 | tmp_match = full_match; | ||
81 | if (cal_start > gdp.start) tmp_match -= (cal_start - gdp.start); | ||
82 | if (first_part || (best_match < tmp_match) || (best_match === tmp_match && tmp_step_diff < best_part_step_diff)) { | ||
83 | first_part = 0; | ||
84 | best_match = tmp_match; | ||
85 | best_part_step_diff = tmp_step_diff; | ||
86 | best_part_rra = i; | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if (first_full === 0) chosen_rra = best_full_rra; | ||
93 | else if (first_part === 0) chosen_rra = best_part_rra; | ||
94 | else throw "the RRD does not contain an RRA matching the chosen CF"; | ||
95 | |||
96 | var rra_info = rrd.getRRAInfo(chosen_rra); | ||
97 | var rra = rrd.getRRA(chosen_rra); | ||
98 | |||
99 | ft_step = rrd.rrd_header.pdp_step * rra_info.getPdpPerRow(); | ||
100 | gdp.start -= (gdp.start % ft_step); | ||
101 | gdp.end += (ft_step - gdp.end % ft_step); | ||
102 | rows = (gdp.end - gdp.start) / ft_step + 1; | ||
103 | |||
104 | gdp.ds_cnt = ds_cnt; | ||
105 | data_ptr = 0; | ||
106 | |||
107 | var rra_end_time = (rrd.getLastUpdate() - (rrd.getLastUpdate() % ft_step)); | ||
108 | var rra_start_time = (rra_end_time - (ft_step * (rra_info.row_cnt - 1))); | ||
109 | /* here's an error by one if we don't be careful */ | ||
110 | var start_offset = (gdp.start + ft_step - rra_start_time) / ft_step; | ||
111 | var end_offset = (rra_end_time - gdp.end) / ft_step; | ||
112 | |||
113 | gdp.data = []; | ||
114 | |||
115 | for (i = start_offset; i < rra.row_cnt - end_offset; i++) { | ||
116 | if (i < 0) { | ||
117 | for (var ii = 0; ii < ds_cnt; ii++) | ||
118 | gdp.data[data_ptr++] = Number.NaN; | ||
119 | } else if (i >= rra.row_cnt) { | ||
120 | for (var ii = 0; ii < ds_cnt; ii++) | ||
121 | gdp.data[data_ptr++] = Number.NaN; | ||
122 | } else { | ||
123 | for (var ii = 0; ii < ds_cnt; ii++) | ||
124 | gdp.data[data_ptr++] = rra.getEl(i, ii); | ||
125 | } | ||
126 | } | ||
127 | return ft_step; | ||
128 | } | ||
129 | }; | ||
diff --git a/js/RrdGfxCanvas.js b/js/RrdGfxCanvas.js new file mode 100644 index 0000000..070b806 --- /dev/null +++ b/js/RrdGfxCanvas.js | |||
@@ -0,0 +1,254 @@ | |||
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 <manuel.luis at gmail.com> | ||
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 | }; | ||
32 | |||
33 | RrdGfxCanvas.prototype.size = function (width, height) | ||
34 | { | ||
35 | this.canvas.width = width; | ||
36 | this.canvas.height = height; | ||
37 | }; | ||
38 | |||
39 | RrdGfxCanvas.prototype.set_dash = function (dashes, n, offset) | ||
40 | { | ||
41 | |||
42 | }; | ||
43 | |||
44 | RrdGfxCanvas.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
45 | { | ||
46 | X0 = Math.round(X0); | ||
47 | Y0 = Math.round(Y0); | ||
48 | X1 = Math.round(X1); | ||
49 | Y1 = Math.round(Y1); | ||
50 | |||
51 | if (Y0 === Y1) { | ||
52 | Y0 += 0.5; | ||
53 | Y1 += 0.5; | ||
54 | } else if (X0 === X1) { | ||
55 | X0 += 0.5; | ||
56 | X1 += 0.5; | ||
57 | } | ||
58 | this.ctx.save(); | ||
59 | this.ctx.lineWidth = width; | ||
60 | this.ctx.strokeStyle = color | ||
61 | this.ctx.beginPath(); | ||
62 | this.ctx.moveTo(X0, Y0); | ||
63 | this.ctx.lineTo(X1, Y1); | ||
64 | this.ctx.stroke(); | ||
65 | this.ctx.restore(); | ||
66 | }; | ||
67 | |||
68 | RrdGfxCanvas.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
69 | { | ||
70 | X0 = Math.round(X0); | ||
71 | Y0 = Math.round(Y0); | ||
72 | X1 = Math.round(X1); | ||
73 | Y1 = Math.round(Y1); | ||
74 | |||
75 | this.ctx.save(); | ||
76 | this.ctx.lineWidth = width; | ||
77 | this.ctx.strokeStyle = color; | ||
78 | |||
79 | this.ctx.beginPath(); | ||
80 | if (Y0 === Y1) { | ||
81 | Y0 += 0.5; | ||
82 | Y1 += 0.5; | ||
83 | if (X0 > X1) { | ||
84 | var swap = X0; | ||
85 | X0 = X1; | ||
86 | X1 = swap; | ||
87 | } | ||
88 | this.ctx.moveTo(X0, Y0); | ||
89 | var n=0; | ||
90 | while(X0<=X1) { | ||
91 | if (n%2 === 1) { | ||
92 | X0 += dash_on; | ||
93 | this.ctx.lineTo(X0, Y0); | ||
94 | } else { | ||
95 | X0 += dash_off; | ||
96 | this.ctx.moveTo(X0, Y0); | ||
97 | } | ||
98 | n++; | ||
99 | } | ||
100 | } else if (X0 === X1) { | ||
101 | X0 += 0.5; | ||
102 | X1 += 0.5; | ||
103 | if (Y0 > Y1) { | ||
104 | var swap = Y0; | ||
105 | Y0 = Y1; | ||
106 | Y1 = swap; | ||
107 | } | ||
108 | this.ctx.moveTo(X0, Y0); | ||
109 | var n=0; | ||
110 | while(Y0<=Y1) { | ||
111 | if (n%2 === 1) { | ||
112 | Y0 += dash_on; | ||
113 | this.ctx.lineTo(X0, Y0); | ||
114 | } else { | ||
115 | Y0 += dash_off; | ||
116 | this.ctx.moveTo(X0, Y0); | ||
117 | } | ||
118 | n++; | ||
119 | } | ||
120 | |||
121 | } else { | ||
122 | this.ctx.moveTo(X0, Y0); | ||
123 | this.ctx.lineTo(X1, Y1); | ||
124 | } | ||
125 | this.ctx.stroke(); | ||
126 | this.ctx.restore(); | ||
127 | }; | ||
128 | |||
129 | RrdGfxCanvas.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
130 | { | ||
131 | X0 = Math.round(X0)+0.5; | ||
132 | X1 = Math.round(X1)+0.5; | ||
133 | Y0 = Math.round(Y0)+0.5; | ||
134 | Y1 = Math.round(Y1)+0.5; | ||
135 | |||
136 | this.ctx.save(); | ||
137 | this.ctx.beginPath(); | ||
138 | this.ctx.lineWidth = width; | ||
139 | this.ctx.moveTo(X0, Y0); | ||
140 | this.ctx.lineTo(X1, Y0); | ||
141 | this.ctx.lineTo(X1, Y1); | ||
142 | this.ctx.lineTo(X0, Y1); | ||
143 | this.ctx.closePath(); | ||
144 | this.ctx.strokeStyle = style; | ||
145 | this.ctx.stroke(); | ||
146 | this.ctx.restore(); | ||
147 | }; | ||
148 | |||
149 | RrdGfxCanvas.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
150 | { | ||
151 | X0 = Math.round(X0)+0.5; | ||
152 | Y0 = Math.round(Y0)+0.5; | ||
153 | X1 = Math.round(X1)+0.5; | ||
154 | Y1 = Math.round(Y1)+0.5; | ||
155 | X2 = Math.round(X2)+0.5; | ||
156 | Y2 = Math.round(Y2)+0.5; | ||
157 | this.ctx.fillStyle = color; | ||
158 | this.ctx.beginPath(); | ||
159 | this.ctx.moveTo(X0, Y0); | ||
160 | this.ctx.lineTo(X1, Y1); | ||
161 | this.ctx.lineTo(X2, Y2); | ||
162 | }; | ||
163 | |||
164 | RrdGfxCanvas.prototype.add_point = function (x, y) | ||
165 | { | ||
166 | x = Math.round(x)+0.5; | ||
167 | y = Math.round(y)+0.5; | ||
168 | this.ctx.lineTo(x, y); | ||
169 | }; | ||
170 | |||
171 | RrdGfxCanvas.prototype.close_path = function () | ||
172 | { | ||
173 | this.ctx.closePath(); | ||
174 | this.ctx.fill(); | ||
175 | }; | ||
176 | |||
177 | RrdGfxCanvas.prototype.stroke_begin = function (width, style) | ||
178 | { | ||
179 | this.ctx.save(); | ||
180 | this.ctx.beginPath(); | ||
181 | this.ctx.lineWidth = width; | ||
182 | this.ctx.strokeStyle = style; | ||
183 | this.ctx.lineCap = 'round'; | ||
184 | this.ctx.round = 'round'; | ||
185 | }; | ||
186 | |||
187 | RrdGfxCanvas.prototype.stroke_end = function () | ||
188 | { | ||
189 | this.ctx.stroke(); | ||
190 | this.ctx.restore(); | ||
191 | }; | ||
192 | |||
193 | RrdGfxCanvas.prototype.moveTo = function (x,y) | ||
194 | { | ||
195 | x = Math.round(x)+0.5; | ||
196 | y = Math.round(y)+0.5; | ||
197 | this.ctx.moveTo(x, y); | ||
198 | }; | ||
199 | |||
200 | RrdGfxCanvas.prototype.lineTo = function (x,y) | ||
201 | { | ||
202 | x = Math.round(x)+0.5; | ||
203 | y = Math.round(y)+0.5; | ||
204 | this.ctx.lineTo(x, y) | ||
205 | }; | ||
206 | |||
207 | RrdGfxCanvas.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
208 | { | ||
209 | x = Math.round(x); | ||
210 | y = Math.round(y); | ||
211 | |||
212 | this.ctx.save(); | ||
213 | this.ctx.font = font.size+'px '+"'"+font.font+"'"; | ||
214 | |||
215 | switch (h_align) { | ||
216 | case RrdGraph.GFX_H_LEFT: | ||
217 | this.ctx.textAlign = 'left'; | ||
218 | break; | ||
219 | case RrdGraph.GFX_H_RIGHT: | ||
220 | this.ctx.textAlign = 'right'; | ||
221 | break; | ||
222 | case RrdGraph.GFX_H_CENTER: | ||
223 | this.ctx.textAlign = 'center'; | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | switch (v_align) { | ||
228 | case RrdGraph.GFX_V_TOP: | ||
229 | this.ctx.textBaseline = 'top'; | ||
230 | break; | ||
231 | case RrdGraph.GFX_V_BOTTOM: | ||
232 | this.ctx.textBaseline = 'bottom'; | ||
233 | break; | ||
234 | case RrdGraph.GFX_V_CENTER: | ||
235 | this.ctx.textBaseline = 'middle'; | ||
236 | break; | ||
237 | } | ||
238 | |||
239 | this.ctx.fillStyle = color; | ||
240 | this.ctx.translate(x,y); | ||
241 | this.ctx.rotate(-angle*Math.PI/180.0); | ||
242 | this.ctx.fillText(text, 0, 0); | ||
243 | this.ctx.restore(); | ||
244 | }; | ||
245 | |||
246 | RrdGfxCanvas.prototype.get_text_width = function(start, font, tabwidth, text) | ||
247 | { | ||
248 | this.ctx.save(); | ||
249 | this.ctx.font = font.size+"px "+font.font; | ||
250 | var width = this.ctx.measureText(text); | ||
251 | this.ctx.restore(); | ||
252 | return width.width; | ||
253 | }; | ||
254 | |||
diff --git a/js/RrdGfxPdf.js b/js/RrdGfxPdf.js new file mode 100644 index 0000000..9203020 --- /dev/null +++ b/js/RrdGfxPdf.js | |||
@@ -0,0 +1,1014 @@ | |||
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 | * FPDF * | ||
20 | * * | ||
21 | * Version: 1.7 * | ||
22 | * Date: 2011-06-18 * | ||
23 | * Author: Olivier PLATHEY * | ||
24 | *******************************************************************************/ | ||
25 | |||
26 | // define('FPDF_VERSION','1.7'); | ||
27 | |||
28 | "use strict"; | ||
29 | |||
30 | /** | ||
31 | * RrdGfxPdf | ||
32 | * @constructor | ||
33 | */ | ||
34 | var RrdGfxPdf = function (orientation, unit, size) | ||
35 | { | ||
36 | if (orientation === undefined) | ||
37 | orientation='P'; | ||
38 | if (unit === undefined) | ||
39 | unit='mm'; | ||
40 | if (size === undefined) | ||
41 | size='A4'; | ||
42 | |||
43 | this.lMargin = 0; // left margin | ||
44 | this.tMargin = 0; // top margin | ||
45 | this.rMargin = 0; // right margin | ||
46 | this.bMargin = 0; // page break margin | ||
47 | this.cMargin = 0; // cell margin | ||
48 | |||
49 | this.x = 0; // current position in user unit | ||
50 | this.y = 0; | ||
51 | |||
52 | this.ZoomMode = null; // zoom display mode | ||
53 | this.LayoutMode = null; // layout display mode | ||
54 | this.title = null; // title | ||
55 | this.subject = null; // subject | ||
56 | this.author = null; // author | ||
57 | this.keywords = null; // keywords | ||
58 | this.creator = null; // creator | ||
59 | |||
60 | // Initialization of properties | ||
61 | this.page = 0; // current page number | ||
62 | this.offsets = []; // array of object offsets | ||
63 | this.n = 2; // current object number | ||
64 | this.buffer = ''; // buffer holding in-memory PDF | ||
65 | this.pages = []; // array containing pages | ||
66 | this.PageSizes = []; // used for pages with non default sizes or orientations | ||
67 | this.state = 0; // current document state | ||
68 | this.fonts = {}; // array of used fonts | ||
69 | this.diffs = []; // array of encoding differences | ||
70 | this.FontFamily = ''; // current font family | ||
71 | this.FontStyle = ''; // current font style | ||
72 | this.FontSizePt = 12; // current font size in points | ||
73 | this.FontSize = this.FontSizePt/this.k; | ||
74 | this.DrawColor = '0 G'; // commands for drawing color | ||
75 | this.FillColor = '0 g'; // commands for filling color | ||
76 | this.TextColor = '0 g'; // commands for text color | ||
77 | this.ColorFlag = false; // indicates whether fill and text colors are different | ||
78 | this.ws = 0; // word spacing | ||
79 | |||
80 | // Core fonts | ||
81 | this.CoreFonts = ['courier', 'helvetica', 'times', 'symbol', 'zapfdingbats']; | ||
82 | // Scale factor (number of points in user unit) | ||
83 | if(unit === 'pt') | ||
84 | this.k = 1; | ||
85 | else if(unit === 'mm') | ||
86 | this.k = 72/25.4; | ||
87 | else if(unit === 'cm') | ||
88 | this.k = 72/2.54; | ||
89 | else if(unit === 'in') | ||
90 | this.k = 72; | ||
91 | else | ||
92 | throw 'Incorrect unit: '+unit; | ||
93 | // Page sizes | ||
94 | this.StdPageSizes = { | ||
95 | 'a3': [841.89 , 1190.55], | ||
96 | 'a4': [595.28 , 841.89], | ||
97 | 'a5': [420.94 , 595.28], | ||
98 | 'letter': [612 , 792], | ||
99 | 'legal': [612 , 1008] | ||
100 | }; | ||
101 | |||
102 | size = this._getpagesize(size); | ||
103 | this.DefPageSize = size; | ||
104 | this.CurPageSize = size; | ||
105 | // Page orientation | ||
106 | orientation = orientation.toLowerCase(); | ||
107 | if(orientation=='p' || orientation=='portrait') { | ||
108 | this.DefOrientation = 'P'; | ||
109 | this.w = size[0]; | ||
110 | this.h = size[1]; | ||
111 | } else if(orientation=='l' || orientation=='landscape') { | ||
112 | this.DefOrientation = 'L'; | ||
113 | this.w = size[1]; | ||
114 | this.h = size[0]; | ||
115 | } else { | ||
116 | throw 'Incorrect orientation: '+orientation; | ||
117 | } | ||
118 | this.CurOrientation = this.DefOrientation; | ||
119 | this.wPt = this.w*this.k; | ||
120 | this.hPt = this.h*this.k; | ||
121 | // Page margins (1 cm) | ||
122 | var margin = 28.35/this.k; | ||
123 | this.SetMargins(margin,margin); | ||
124 | // Interior cell margin (1 mm) | ||
125 | this.cMargin = margin/10; | ||
126 | // Line width (0.2 mm) | ||
127 | this.LineWidth = .567/this.k; | ||
128 | // Default display mode | ||
129 | this.SetDisplayMode('default'); | ||
130 | // Set default PDF version number | ||
131 | this.PDFVersion = '1.3'; | ||
132 | }; | ||
133 | |||
134 | RrdGfxPdf.CORE_FONTS= { | ||
135 | 'courierBI': {name: 'Courier-BoldOblique', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
136 | 'courierB': {name: 'Courier-Bold', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
137 | 'courierI': {name: 'Courier-Oblique', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
138 | 'courier': {name: 'Courier', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
139 | 'helveticaBI': {name: 'Helvetica-BoldOblique', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722, 722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889, 611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556, 500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000, 556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611, 611,611,611,611,611,584,611,611,611,611,611,556,611,556] }, | ||
140 | 'helveticaB': {name: 'Helvetica-Bold', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722, 722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889, 611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556, 500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000, 556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611, 611,611,611,611,611,584,611,611,611,611,611,556,611,556] }, | ||
141 | 'helveticaI': {name: 'Helvetica-Oblique', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667, 667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833, 556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556, 333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000, 500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556, 556,556,556,556,556,584,611,556,556,556,556,500,556,500] }, | ||
142 | 'helvetica': {name: 'Helvetica', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667, 667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833, 556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556, 333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000, 500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556, 556,556,556,556,556,584,611,556,556,556,556,500,556,500] }, | ||
143 | 'timesBI': {name: 'Times-BoldItalic', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667, 667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889, 667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778, 556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500, 500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000, 389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333, 400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667, 944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722, 722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556, 500,500,500,500,500,570,500,556,556,556,556,444,500,444] }, | ||
144 | 'timesB': {name: 'Times-Bold', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722, 667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000, 722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833, 556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500, 500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000, 389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333, 400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722, 1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722, 722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556, 500,500,500,500,500,570,500,556,556,556,556,500,556,500] }, | ||
145 | 'timesI': {name: 'Times-Italic', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611, 611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833, 611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722, 500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500, 556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980, 389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333, 400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611, 889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722, 722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500, 500,500,500,500,500,675,500,500,500,500,500,444,500,444] }, | ||
146 | 'times': {name: 'Times-Roman', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722, 667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944, 722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778, 500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500, 444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980, 389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333, 400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722, 889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722, 722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500, 500,500,500,500,500,564,500,500,500,500,500,500,500,500] }, | ||
147 | 'symbol': {name: 'Symbol', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,713,500,549,833,778,439,333,333,500,549, 250,549,250,278,500,500,500,500,500,500,500,500,500,500,278,278,549,549,549,444,549,722, 667,722,612,611,763,603,722,333,631,722,686,889,722,722,768,741,556,592,611,690,439,768, 645,795,611,333,863,333,658,500,500,631,549,549,494,439,521,411,603,329,603,549,549,576, 521,549,549,521,549,603,439,576,713,686,493,686,494,480,200,480,549,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,750,620,247,549,167,713,500,753,753,753,753,1042,987,603,987,603, 400,549,411,549,549,713,494,460,549,549,549,549,1000,603,1000,658,823,686,795,987,768,768, 823,768,768,713,713,713,713,713,713,713,768,713,790,790,890,823,549,250,713,603,603,1042, 987,603,987,603,494,329,790,790,786,713,384,384,384,384,384,384,494,494,494,494,0,329,274,686,686,686,384,384,384,384,384,384,494,494,494,0] }, | ||
148 | 'zapfdingbats': {name: 'ZapfDingbats', up: -100, ut: 50, cw: [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939, 549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692, 786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776, 768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873, 761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317, 276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788, 788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788, 788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918, 927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874, 760,946,771,865,771,888,967,888,831,873,927,970,918,0] } | ||
149 | }; | ||
150 | |||
151 | RrdGfxPdf.prototype.parse_color = function(str) | ||
152 | { | ||
153 | var bits; | ||
154 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) { | ||
155 | return [parseInt(bits[1]+bits[1], 16), parseInt(bits[2]+bits[2], 16), parseInt(bits[3]+bits[3], 16), 1.0]; | ||
156 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
157 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), 1.0]; | ||
158 | } 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))) { | ||
159 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), parseInt(bits[4], 16)/255]; | ||
160 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) { | ||
161 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), 1.0]; | ||
162 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) { | ||
163 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), parseFloat(bits[4], 10)]; | ||
164 | } else { | ||
165 | throw "Unknow color format '"+str+"'"; | ||
166 | } | ||
167 | }; | ||
168 | |||
169 | RrdGfxPdf.prototype.size = function (width, height) | ||
170 | { | ||
171 | var size = [height, width]; | ||
172 | this.DefPageSize = size; | ||
173 | this.CurPageSize = size; | ||
174 | this.DefOrientation = 'L'; | ||
175 | this.w = size[1]; | ||
176 | this.h = size[0]; | ||
177 | this.CurOrientation = this.DefOrientation; | ||
178 | this.wPt = this.w*this.k; | ||
179 | this.hPt = this.h*this.k; | ||
180 | this.AddPage(); | ||
181 | }; | ||
182 | |||
183 | RrdGfxPdf.prototype.set_dash = function (dashes, n, offset) | ||
184 | { | ||
185 | }; | ||
186 | |||
187 | RrdGfxPdf.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
188 | { | ||
189 | this._save(); | ||
190 | this._setLineWidth(width); | ||
191 | var rgba = this.parse_color(color); | ||
192 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
193 | this._moveTo(X0, Y0); | ||
194 | this._lineTo(X1, Y1); | ||
195 | this._stroke(); | ||
196 | this._restore(); | ||
197 | }; | ||
198 | |||
199 | RrdGfxPdf.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
200 | { | ||
201 | this._save(); | ||
202 | this._setLineWidth(width); | ||
203 | var rgba = this.parse_color(color); | ||
204 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
205 | this._out('['+(dash_on*this.k)+' '+(dash_off*this.k)+'] 0 d'); | ||
206 | this._moveTo(X0, Y0); | ||
207 | this._lineTo(X1, Y1); | ||
208 | this._stroke(); | ||
209 | this._restore(); | ||
210 | }; | ||
211 | |||
212 | RrdGfxPdf.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
213 | { | ||
214 | this._save(); | ||
215 | this._setLineWidth(width); | ||
216 | var rgba = this.parse_color(style); | ||
217 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
218 | this._moveTo(X0, Y0); | ||
219 | this._lineTo(X1, Y0); | ||
220 | this._lineTo(X1, Y1); | ||
221 | this._lineTo(X0, Y1); | ||
222 | this._closePath(); | ||
223 | this._stroke(); | ||
224 | this._restore(); | ||
225 | }; | ||
226 | |||
227 | RrdGfxPdf.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
228 | { | ||
229 | var rgba = this.parse_color(color); | ||
230 | this._setFillColor(rgba[0], rgba[1], rgba[2]); | ||
231 | this._moveTo(X0, Y0); | ||
232 | this._lineTo(X1, Y1); | ||
233 | this._lineTo(X2, Y2); | ||
234 | }; | ||
235 | |||
236 | RrdGfxPdf.prototype.add_point = function (x, y) | ||
237 | { | ||
238 | this._lineTo(x, y); | ||
239 | }; | ||
240 | |||
241 | RrdGfxPdf.prototype.close_path = function () | ||
242 | { | ||
243 | this._closePath(); | ||
244 | this._fill(); | ||
245 | }; | ||
246 | |||
247 | RrdGfxPdf.prototype.stroke_begin = function (width, style) | ||
248 | { | ||
249 | this._save(); | ||
250 | this._setLineWidth(width); | ||
251 | var rgba = this.parse_color(style); | ||
252 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
253 | this._out('0 J'); // line cap | ||
254 | this._out('0 j'); // line join | ||
255 | }; | ||
256 | |||
257 | RrdGfxPdf.prototype.stroke_end = function () | ||
258 | { | ||
259 | this._stroke(); | ||
260 | this._restore(); | ||
261 | }; | ||
262 | |||
263 | RrdGfxPdf.prototype.moveTo = function (x,y) | ||
264 | { | ||
265 | this._moveTo(x, y); | ||
266 | }; | ||
267 | |||
268 | RrdGfxPdf.prototype.lineTo = function (x,y) | ||
269 | { | ||
270 | this._lineTo(x, y) | ||
271 | }; | ||
272 | |||
273 | RrdGfxPdf.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
274 | { | ||
275 | this._save(); | ||
276 | this._setFont('courier', '', font.size*this.k); | ||
277 | |||
278 | var width = this._getStringWidth('courier', '', font.size*this.k, text); | ||
279 | var height = font.size; | ||
280 | /* | ||
281 | this._moveTo(x,y-5); this._lineTo(x,y+5); | ||
282 | this._moveTo(x-5,y); this._lineTo(x+5,y); | ||
283 | this._moveTo(x+width,y-height-5); this._lineTo(x+width,y-height+5); | ||
284 | this._moveTo(x+width-5,y-height); this._lineTo(x+width+5,y-height); | ||
285 | this._stroke(); | ||
286 | */ | ||
287 | switch (h_align) { | ||
288 | case RrdGraph.GFX_H_LEFT: | ||
289 | if (angle == -90) { | ||
290 | x = x-height/2; | ||
291 | } | ||
292 | break; | ||
293 | case RrdGraph.GFX_H_RIGHT: | ||
294 | x = x-width; | ||
295 | break; | ||
296 | case RrdGraph.GFX_H_CENTER: | ||
297 | if (angle != 90) { | ||
298 | x = x-width/2; | ||
299 | } | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | switch (v_align) { | ||
304 | case RrdGraph.GFX_V_TOP: | ||
305 | if (angle != -90) { | ||
306 | y = y + height/2; | ||
307 | } | ||
308 | break; | ||
309 | case RrdGraph.GFX_V_BOTTOM: | ||
310 | y = y - height/3; | ||
311 | break; | ||
312 | case RrdGraph.GFX_V_CENTER: | ||
313 | if (angle == 90) { | ||
314 | y = y + width/2; | ||
315 | } else { | ||
316 | y = y + height/4; | ||
317 | } | ||
318 | break; | ||
319 | } | ||
320 | |||
321 | x = x*this.k; | ||
322 | y = (this.h-y)*this.k | ||
323 | |||
324 | var tm = []; | ||
325 | tm[0] = Math.cos(angle*Math.PI/180.0); | ||
326 | tm[1] = Math.sin(angle*Math.PI/180.0); | ||
327 | tm[2] = -tm[1]; | ||
328 | tm[3] = tm[0]; | ||
329 | |||
330 | tm[4] = x + (tm[1] * y) - (tm[0] * x); | ||
331 | tm[5] = y - (tm[0] * y) - (tm[1] * x); | ||
332 | |||
333 | var rgba = this.parse_color(color); | ||
334 | this._save(); | ||
335 | this._out('BT'); | ||
336 | this._out(sprintf('%.3F %.3F %.3F rg', rgba[0]/255,rgba[1]/255,rgba[2]/255)); | ||
337 | this._out(sprintf('%.2F %.2F Td', x, y)); | ||
338 | this._out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', tm[0], tm[1], tm[2], tm[3], tm[4], tm[5])); | ||
339 | this._out(sprintf('(%s) Tj',this._escape(text))); | ||
340 | this._out('ET'); | ||
341 | this._restore(); | ||
342 | }; | ||
343 | |||
344 | RrdGfxPdf.prototype.get_text_width = function(start, font, tabwidth, text) | ||
345 | { | ||
346 | var width = this._getStringWidth('courier', '', font.size*this.k, text); | ||
347 | return width; | ||
348 | }; | ||
349 | |||
350 | /**** Public methods *****/ | ||
351 | |||
352 | RrdGfxPdf.prototype.SetMargins = function(left, top, right) | ||
353 | { | ||
354 | if (right === undefined) | ||
355 | right = null; | ||
356 | // Set left, top and right margins | ||
357 | this.lMargin = left; | ||
358 | this.tMargin = top; | ||
359 | if(right===null) | ||
360 | right = left; | ||
361 | this.rMargin = right; | ||
362 | }; | ||
363 | |||
364 | RrdGfxPdf.prototype.SetLeftMargin = function(margin) | ||
365 | { | ||
366 | // Set left margin | ||
367 | this.lMargin = margin; | ||
368 | if(this.page>0 && this.x<margin) | ||
369 | this.x = margin; | ||
370 | }; | ||
371 | |||
372 | RrdGfxPdf.prototype.SetTopMargin = function(margin) | ||
373 | { | ||
374 | // Set top margin | ||
375 | this.tMargin = margin; | ||
376 | }; | ||
377 | |||
378 | RrdGfxPdf.prototype.SetRightMargin = function(margin) | ||
379 | { | ||
380 | // Set right margin | ||
381 | this.rMargin = margin; | ||
382 | }; | ||
383 | |||
384 | RrdGfxPdf.prototype.SetDisplayMode = function(zoom, layout) | ||
385 | { | ||
386 | // Set display mode in viewer | ||
387 | if(zoom === 'fullpage' || zoom === 'fullwidth' || zoom === 'real' || zoom == 'default' || !(typeof zoom === "string")) | ||
388 | this.ZoomMode = zoom; | ||
389 | else | ||
390 | throw 'Incorrect zoom display mode: '+zoom; | ||
391 | |||
392 | if(layout === undefined) { | ||
393 | this.LayoutMode = 'default'; | ||
394 | } else if(layout === 'single' || layout === 'continuous' || layout === 'two' || layout === 'default') { | ||
395 | this.LayoutMode = layout; | ||
396 | } else { | ||
397 | throw 'Incorrect layout display mode: '+layout; | ||
398 | } | ||
399 | }; | ||
400 | |||
401 | RrdGfxPdf.prototype.SetTitle = function(title) | ||
402 | { | ||
403 | // Title of document | ||
404 | this.title = title; | ||
405 | }; | ||
406 | |||
407 | RrdGfxPdf.prototype.SetSubject = function(subject) | ||
408 | { | ||
409 | // Subject of document | ||
410 | this.subject = subject; | ||
411 | }; | ||
412 | |||
413 | RrdGfxPdf.prototype.SetAuthor = function(author) | ||
414 | { | ||
415 | // Author of document | ||
416 | this.author = author; | ||
417 | }; | ||
418 | |||
419 | RrdGfxPdf.prototype.SetKeywords = function(keywords) | ||
420 | { | ||
421 | // Keywords of document | ||
422 | this.keywords = keywords; | ||
423 | }; | ||
424 | |||
425 | RrdGfxPdf.prototype.SetCreator = function(creator) | ||
426 | { | ||
427 | // Creator of document | ||
428 | this.creator = creator; | ||
429 | }; | ||
430 | |||
431 | RrdGfxPdf.prototype.Open = function() | ||
432 | { | ||
433 | // Begin document | ||
434 | this.state = 1; | ||
435 | }; | ||
436 | |||
437 | RrdGfxPdf.prototype.Close = function() | ||
438 | { | ||
439 | // Terminate document | ||
440 | if(this.state==3) | ||
441 | return; | ||
442 | if(this.page==0) | ||
443 | this.AddPage(); | ||
444 | // Close page | ||
445 | this._endpage(); | ||
446 | // Close document | ||
447 | this._enddoc(); | ||
448 | }; | ||
449 | |||
450 | RrdGfxPdf.prototype.AddPage = function(orientation, size) | ||
451 | { | ||
452 | if (orientation === undefined) orientation=''; | ||
453 | if (size === undefined) size=''; | ||
454 | |||
455 | // Start a new page | ||
456 | if(this.state==0) | ||
457 | this.Open(); | ||
458 | |||
459 | var family = this.FontFamily; | ||
460 | var style = this.FontStyle; | ||
461 | var fontsize = this.FontSizePt; | ||
462 | var lw = this.LineWidth; | ||
463 | var dc = this.DrawColor; | ||
464 | var fc = this.FillColor; | ||
465 | var tc = this.TextColor; | ||
466 | var cf = this.ColorFlag; | ||
467 | |||
468 | if(this.page>0) | ||
469 | { | ||
470 | // Close page | ||
471 | this._endpage(); | ||
472 | } | ||
473 | // Start new page | ||
474 | this._beginpage(orientation,size); | ||
475 | // Set line cap style to square | ||
476 | this._out('2 J'); | ||
477 | // Set line width | ||
478 | this.LineWidth = lw; | ||
479 | this._out(sprintf('%.2F w',lw*this.k)); | ||
480 | // Set font | ||
481 | if(family) | ||
482 | this._setFont(family,style,fontsize); | ||
483 | // Set colors | ||
484 | this.DrawColor = dc; | ||
485 | if(dc!='0 G') | ||
486 | this._out(dc); | ||
487 | this.FillColor = fc; | ||
488 | if(fc!='0 g') | ||
489 | this._out(fc); | ||
490 | this.TextColor = tc; | ||
491 | this.ColorFlag = cf; | ||
492 | // Restore line width | ||
493 | if(this.LineWidth!=lw) | ||
494 | { | ||
495 | this.LineWidth = lw; | ||
496 | this._out(sprintf('%.2F w',lw*this.k)); | ||
497 | } | ||
498 | // Restore font | ||
499 | if(family) | ||
500 | this._setFont(family,style,fontsize); | ||
501 | // Restore colors | ||
502 | if(this.DrawColor!=dc) | ||
503 | { | ||
504 | this.DrawColor = dc; | ||
505 | this._out(dc); | ||
506 | } | ||
507 | if(this.FillColor!=fc) | ||
508 | { | ||
509 | this.FillColor = fc; | ||
510 | this._out(fc); | ||
511 | } | ||
512 | this.TextColor = tc; | ||
513 | this.ColorFlag = cf; | ||
514 | }; | ||
515 | |||
516 | RrdGfxPdf.prototype.PageNo = function() | ||
517 | { | ||
518 | // Get current page number | ||
519 | return this.page; | ||
520 | }; | ||
521 | |||
522 | RrdGfxPdf.prototype._setDrawColor = function(r, g, b) | ||
523 | { | ||
524 | if (g === undefined) g=null; | ||
525 | if (b === undefined) b=null; | ||
526 | // Set color for all stroking operations | ||
527 | if((r==0 && g==0 && b==0) || g===null) | ||
528 | this.DrawColor = sprintf('%.3F G',r/255); | ||
529 | else | ||
530 | this.DrawColor = sprintf('%.3F %.3F %.3F RG',r/255,g/255,b/255); | ||
531 | if(this.page>0) | ||
532 | this._out(this.DrawColor); | ||
533 | }; | ||
534 | |||
535 | RrdGfxPdf.prototype._setFillColor = function(r, g, b) | ||
536 | { | ||
537 | if (g === undefined) g=null; | ||
538 | if (b === undefined) b=null; | ||
539 | // Set color for all filling operations | ||
540 | if((r==0 && g==0 && b==0) || g===null) | ||
541 | this.FillColor = sprintf('%.3F g',r/255); | ||
542 | else | ||
543 | this.FillColor = sprintf('%.3F %.3F %.3F rg',r/255,g/255,b/255); | ||
544 | this.ColorFlag = (this.FillColor!=this.TextColor); | ||
545 | if(this.page>0) | ||
546 | this._out(this.FillColor); | ||
547 | }; | ||
548 | |||
549 | RrdGfxPdf.prototype._setTextColor = function(r, g, b) | ||
550 | { | ||
551 | if (g === undefined) g=null; | ||
552 | if (b === undefined) b=null; | ||
553 | // Set color for text | ||
554 | if((r==0 && g==0 && b==0) || g===null) | ||
555 | this.TextColor = sprintf('%.3F g',r/255); | ||
556 | else | ||
557 | this.TextColor = sprintf('%.3F %.3F %.3F rg',r/255,g/255,b/255); | ||
558 | this.ColorFlag = (this.FillColor!=this.TextColor); | ||
559 | }; | ||
560 | |||
561 | RrdGfxPdf.prototype._getStringWidth = function(family, style, size, s) | ||
562 | { | ||
563 | if (style === undefined) style = ''; | ||
564 | if (size === undefined) size = 0; | ||
565 | // Select a font; size given in points | ||
566 | |||
567 | if(family=='') family = this.FontFamily; | ||
568 | else family = family.toLowerCase(); | ||
569 | |||
570 | style = style.toUpperCase(); | ||
571 | if(style=='IB') style = 'BI'; | ||
572 | |||
573 | if(size==0) size = this.FontSizePt; | ||
574 | |||
575 | // Test if font is already loaded | ||
576 | var fontkey = family+style; | ||
577 | if(!(fontkey in this.fonts)) { | ||
578 | // Test if one of the core fonts | ||
579 | if(family=='arial') family = 'helvetica'; | ||
580 | if(family=='symbol' || family=='zapfdingbats') style = ''; | ||
581 | fontkey = family+style; | ||
582 | |||
583 | if (!(fontkey in this.fonts)) | ||
584 | this.AddFont(family, style); | ||
585 | } | ||
586 | // Select it | ||
587 | size = size/this.k; | ||
588 | var cw = this.fonts[fontkey].cw; | ||
589 | var w = 0; | ||
590 | var l = s.length; | ||
591 | for(var i=0; i<l; i++) { | ||
592 | w += cw[s.charCodeAt(i)]; | ||
593 | } | ||
594 | return w*size/1000; | ||
595 | }; | ||
596 | |||
597 | RrdGfxPdf.prototype._setLineWidth = function(width) | ||
598 | { | ||
599 | // Set line width | ||
600 | this.LineWidth = width; | ||
601 | if(this.page>0) | ||
602 | this._out(sprintf('%.2F w',width*this.k)); | ||
603 | }; | ||
604 | |||
605 | RrdGfxPdf.prototype._moveTo = function(x, y) | ||
606 | { | ||
607 | this._out(sprintf('%.2F %.2F m',x*this.k,(this.h-y)*this.k)); | ||
608 | }; | ||
609 | |||
610 | RrdGfxPdf.prototype._lineTo = function(x, y) | ||
611 | { | ||
612 | this._out(sprintf('%.2F %.2F l',x*this.k,(this.h-y)*this.k)); | ||
613 | }; | ||
614 | |||
615 | RrdGfxPdf.prototype._stroke = function() | ||
616 | { | ||
617 | this._out('S'); | ||
618 | }; | ||
619 | |||
620 | RrdGfxPdf.prototype._save = function() | ||
621 | { | ||
622 | this._out('q'); | ||
623 | }; | ||
624 | |||
625 | RrdGfxPdf.prototype._restore = function() | ||
626 | { | ||
627 | this._out('Q'); | ||
628 | }; | ||
629 | |||
630 | RrdGfxPdf.prototype._closePath = function() | ||
631 | { | ||
632 | this._out('h'); | ||
633 | }; | ||
634 | |||
635 | RrdGfxPdf.prototype._fill = function() | ||
636 | { | ||
637 | this._out('f'); | ||
638 | }; | ||
639 | |||
640 | RrdGfxPdf.prototype._line = function(x1, y1, x2, y2) | ||
641 | { | ||
642 | // Draw a line | ||
643 | this._out(sprintf('%.2F %.2F m %.2F %.2F l S',x1*this.k,(this.h-y1)*this.k,x2*this.k,(this.h-y2)*this.k)); | ||
644 | }; | ||
645 | |||
646 | RrdGfxPdf.prototype._rect = function(x, y, w, h, style) | ||
647 | { | ||
648 | var op; | ||
649 | // Draw a rectangle | ||
650 | if(style=='F') | ||
651 | op = 'f'; | ||
652 | else if(style=='FD' || style=='DF') | ||
653 | op = 'B'; | ||
654 | else | ||
655 | op = 'S'; | ||
656 | this._out(sprintf('%.2F %.2F %.2F %.2F re %s',x*this.k,(this.h-y)*this.k,w*this.k,-h*this.k,op)); | ||
657 | }; | ||
658 | |||
659 | RrdGfxPdf.prototype.AddFont = function (family, style, file) | ||
660 | { | ||
661 | if (style === undefined) style = ''; | ||
662 | |||
663 | if(family=='') family = this.FontFamily; | ||
664 | else family = family.toLowerCase(); | ||
665 | |||
666 | style = style.toUpperCase(); | ||
667 | if(style=='IB') style = 'BI'; | ||
668 | |||
669 | var fontkey = family+style; | ||
670 | if(fontkey in this.fonts) | ||
671 | return; | ||
672 | |||
673 | if(fontkey in RrdGfxPdf.CORE_FONTS){ | ||
674 | var font = RrdGfxPdf.CORE_FONTS[fontkey]; | ||
675 | this.fonts[fontkey] = font; | ||
676 | var i=0; | ||
677 | for (var n in this.fonts) i++; | ||
678 | font['i'] = i; | ||
679 | } else { | ||
680 | throw 'Undefined font: '+family+' '+style; | ||
681 | } | ||
682 | }; | ||
683 | |||
684 | RrdGfxPdf.prototype._setFont = function(family, style, size) | ||
685 | { | ||
686 | if (style === undefined) style = ''; | ||
687 | if (size === undefined) size = 0; | ||
688 | // Select a font; size given in points | ||
689 | |||
690 | if(family=='') family = this.FontFamily; | ||
691 | else family = family.toLowerCase(); | ||
692 | |||
693 | style = style.toUpperCase(); | ||
694 | if(style=='IB') style = 'BI'; | ||
695 | |||
696 | if(size==0) size = this.FontSizePt; | ||
697 | |||
698 | // Test if font is already selected | ||
699 | //if(this.FontFamily==family && this.FontStyle==style && this.FontSizePt==size) | ||
700 | // return; | ||
701 | |||
702 | // Test if font is already loaded | ||
703 | var fontkey = family+style; | ||
704 | if(!(fontkey in this.fonts)) { | ||
705 | // Test if one of the core fonts | ||
706 | if(family=='arial') family = 'helvetica'; | ||
707 | if(family=='symbol' || family=='zapfdingbats') style = ''; | ||
708 | fontkey = family+style; | ||
709 | |||
710 | if (!(fontkey in this.fonts)) | ||
711 | this.AddFont(family, style); | ||
712 | } | ||
713 | // Select it | ||
714 | this.FontFamily = family; | ||
715 | this.FontStyle = style; | ||
716 | this.FontSizePt = size; | ||
717 | this.FontSize = size/this.k; | ||
718 | this.CurrentFont = this.fonts[fontkey]; | ||
719 | if(this.page>0) | ||
720 | this._out(sprintf('BT /F%d %.2F Tf ET',this.CurrentFont['i'],this.FontSizePt)); // FIXME i | ||
721 | }; | ||
722 | |||
723 | RrdGfxPdf.prototype._setFontSize = function(size) | ||
724 | { | ||
725 | // Set font size in points | ||
726 | //if(this.FontSizePt==size) | ||
727 | // return; | ||
728 | this.FontSizePt = size; | ||
729 | this.FontSize = size/this.k; | ||
730 | if(this.page>0) | ||
731 | this._out(sprintf('BT /F%d %.2F Tf ET',this.CurrentFont['i'],this.FontSizePt)); | ||
732 | }; | ||
733 | |||
734 | RrdGfxPdf.prototype._text = function(x, y, txt) | ||
735 | { | ||
736 | // Output a string | ||
737 | var s = sprintf('BT %.2F %.2F Td (%s) Tj ET',x*this.k,(this.h-y)*this.k,this._escape(txt)); | ||
738 | if(this.ColorFlag) | ||
739 | s = 'q '+this.TextColor+' '+s+' Q'; | ||
740 | this._out(s); | ||
741 | }; | ||
742 | |||
743 | RrdGfxPdf.prototype.output = function() | ||
744 | { | ||
745 | // Output PDF to some destination | ||
746 | if(this.state<3) | ||
747 | this.Close(); | ||
748 | document.location.href = 'data:application/pdf;base64,' + Base64.encode(this.buffer); | ||
749 | //return this.buffer; | ||
750 | }; | ||
751 | |||
752 | RrdGfxPdf.prototype._getpagesize = function(size) // FIXME | ||
753 | { | ||
754 | if(typeof size === "string" ) { | ||
755 | size = size.toLowerCase(); | ||
756 | if(!(size in this.StdPageSizes)) | ||
757 | throw 'Unknown page size: '+size; | ||
758 | var a = this.StdPageSizes[size]; | ||
759 | return [a[0]/this.k, a[1]/this.k]; | ||
760 | } else { | ||
761 | if(size[0]>size[1]) { | ||
762 | return [size[1], size[0]]; | ||
763 | } else { | ||
764 | return size; | ||
765 | } | ||
766 | } | ||
767 | }; | ||
768 | |||
769 | RrdGfxPdf.prototype._beginpage = function(orientation, size) | ||
770 | { | ||
771 | this.page++; | ||
772 | this.pages[this.page] = ''; | ||
773 | this.state = 2; | ||
774 | this.x = this.lMargin; | ||
775 | this.y = this.tMargin; | ||
776 | this.FontFamily = ''; | ||
777 | // Check page size and orientation | ||
778 | if(orientation=='') orientation = this.DefOrientation; | ||
779 | else orientation = strtoupper(orientation[0]); | ||
780 | |||
781 | if(size=='') size = this.DefPageSize; | ||
782 | else size = this._getpagesize(size); | ||
783 | |||
784 | if(orientation!=this.CurOrientation || size[0]!=this.CurPageSize[0] || size[1]!=this.CurPageSize[1]) | ||
785 | { | ||
786 | // New size or orientation | ||
787 | if(orientation=='P') { | ||
788 | this.w = size[0]; | ||
789 | this.h = size[1]; | ||
790 | } else { | ||
791 | this.w = size[1]; | ||
792 | this.h = size[0]; | ||
793 | } | ||
794 | this.wPt = this.w*this.k; | ||
795 | this.hPt = this.h*this.k; | ||
796 | this.CurOrientation = orientation; | ||
797 | this.CurPageSize = size; | ||
798 | } | ||
799 | if(orientation!=this.DefOrientation || size[0]!=this.DefPageSize[0] || size[1]!=this.DefPageSize[1]) | ||
800 | this.PageSizes[this.page] = [this.wPt, this.hPt]; | ||
801 | }; | ||
802 | |||
803 | RrdGfxPdf.prototype._endpage = function() | ||
804 | { | ||
805 | this.state = 1; | ||
806 | }; | ||
807 | |||
808 | RrdGfxPdf.prototype._escape = function(s) // FIXME | ||
809 | { | ||
810 | // Escape special characters in strings | ||
811 | //s = str_replace('\\','\\\\',s); | ||
812 | //s = str_replace('(','\\(',s); | ||
813 | //s = str_replace(')','\\)',s); | ||
814 | //s = str_replace("\r",'\\r',s); | ||
815 | return s.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); | ||
816 | }; | ||
817 | |||
818 | RrdGfxPdf.prototype._textstring = function(s) | ||
819 | { | ||
820 | // Format a text string | ||
821 | return '('+this._escape(s)+')'; | ||
822 | }; | ||
823 | |||
824 | RrdGfxPdf.prototype._newobj = function() | ||
825 | { | ||
826 | // Begin a new object | ||
827 | this.n++; | ||
828 | this.offsets[this.n] = this.buffer.length; | ||
829 | this._out(this.n+' 0 obj'); | ||
830 | }; | ||
831 | |||
832 | RrdGfxPdf.prototype._putstream = function(s) | ||
833 | { | ||
834 | this._out('stream'); | ||
835 | this._out(s); | ||
836 | this._out('endstream'); | ||
837 | }, | ||
838 | |||
839 | RrdGfxPdf.prototype._out = function(s) | ||
840 | { | ||
841 | // Add a line to the document | ||
842 | if(this.state==2) | ||
843 | this.pages[this.page] += s+"\n"; | ||
844 | else | ||
845 | this.buffer += s+"\n"; | ||
846 | }; | ||
847 | |||
848 | RrdGfxPdf.prototype._putpages = function() | ||
849 | { | ||
850 | var wPt, hPt; | ||
851 | var nb = this.page; | ||
852 | if(this.DefOrientation=='P') { | ||
853 | wPt = this.DefPageSize[0]*this.k; | ||
854 | hPt = this.DefPageSize[1]*this.k; | ||
855 | } else { | ||
856 | wPt = this.DefPageSize[1]*this.k; | ||
857 | hPt = this.DefPageSize[0]*this.k; | ||
858 | } | ||
859 | for(var n=1;n<=nb;n++) | ||
860 | { | ||
861 | // Page | ||
862 | this._newobj(); | ||
863 | this._out('<</Type /Page'); | ||
864 | this._out('/Parent 1 0 R'); | ||
865 | if(this.PageSizes[n] !== undefined) | ||
866 | this._out(sprintf('/MediaBox [0 0 %.2F %.2F]',this.PageSizes[n][0],this.PageSizes[n][1])); | ||
867 | this._out('/Resources 2 0 R'); | ||
868 | if(this.PDFVersion>'1.3') | ||
869 | this._out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>'); | ||
870 | this._out('/Contents '+(this.n+1)+' 0 R>>'); | ||
871 | this._out('endobj'); | ||
872 | // Page content | ||
873 | this._newobj(); | ||
874 | this._out('<</Length '+this.pages[n].length+'>>'); | ||
875 | this._putstream(this.pages[n]); | ||
876 | this._out('endobj'); | ||
877 | } | ||
878 | // Pages root | ||
879 | this.offsets[1] = this.buffer.length; | ||
880 | this._out('1 0 obj'); | ||
881 | this._out('<</Type /Pages'); | ||
882 | var kids = '/Kids ['; | ||
883 | for(var i=0;i<nb;i++) | ||
884 | kids += (3+2*i)+' 0 R '; | ||
885 | this._out(kids+']'); | ||
886 | this._out('/Count '+nb); | ||
887 | this._out(sprintf('/MediaBox [0 0 %.2F %.2F]',wPt,hPt)); | ||
888 | this._out('>>'); | ||
889 | this._out('endobj'); | ||
890 | }; | ||
891 | |||
892 | RrdGfxPdf.prototype._putfonts = function() | ||
893 | { | ||
894 | var nf = this.n; | ||
895 | for(var diff in this.diffs) { | ||
896 | // Encodings | ||
897 | this._newobj(); | ||
898 | this._out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['+diff+']>>'); | ||
899 | this._out('endobj'); | ||
900 | } | ||
901 | for(var font in this.fonts) { // FIXME | ||
902 | // Font objects | ||
903 | this.fonts[font]['n'] = this.n+1; | ||
904 | var name = this.fonts[font]['name']; | ||
905 | // Core font | ||
906 | this._newobj(); | ||
907 | this._out('<</Type /Font'); | ||
908 | this._out('/BaseFont /'+name); | ||
909 | this._out('/Subtype /Type1'); | ||
910 | if(name!='Symbol' && name!='ZapfDingbats') | ||
911 | this._out('/Encoding /WinAnsiEncoding'); | ||
912 | this._out('>>'); | ||
913 | this._out('endobj'); | ||
914 | } | ||
915 | }; | ||
916 | |||
917 | RrdGfxPdf.prototype._putresourcedict = function() | ||
918 | { | ||
919 | this._out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); | ||
920 | this._out('/Font <<'); | ||
921 | for(var font in this.fonts) | ||
922 | this._out('/F'+this.fonts[font]['i']+' '+this.fonts[font]['n']+' 0 R'); | ||
923 | this._out('>>'); | ||
924 | this._out('/XObject <<'); | ||
925 | this._out('>>'); | ||
926 | }; | ||
927 | |||
928 | RrdGfxPdf.prototype._putresources = function() | ||
929 | { | ||
930 | this._putfonts(); | ||
931 | // Resource dictionary | ||
932 | this.offsets[2] = this.buffer.length; | ||
933 | this._out('2 0 obj'); | ||
934 | this._out('<<'); | ||
935 | this._putresourcedict(); | ||
936 | this._out('>>'); | ||
937 | this._out('endobj'); | ||
938 | }; | ||
939 | |||
940 | RrdGfxPdf.prototype._putinfo = function() | ||
941 | { | ||
942 | // this._out('/Producer '+this._textstring('FPDF '+FPDF_VERSION)); FIXME | ||
943 | if(this.title != null) | ||
944 | this._out('/Title '+this._textstring(this.title)); | ||
945 | if(this.subject != null) | ||
946 | this._out('/Subject '+this._textstring(this.subject)); | ||
947 | if(this.author != null) | ||
948 | this._out('/Author '+this._textstring(this.author)); | ||
949 | if(this.keywords != null) | ||
950 | this._out('/Keywords '+this._textstring(this.keywords)); | ||
951 | if(this.creator != null) | ||
952 | this._out('/Creator '+this._textstring(this.creator)); | ||
953 | // this._out('/CreationDate '+this._textstring('D:'+date('YmdHis'))); // FIXME | ||
954 | }; | ||
955 | |||
956 | RrdGfxPdf.prototype._putcatalog = function() | ||
957 | { | ||
958 | this._out('/Type /Catalog'); | ||
959 | this._out('/Pages 1 0 R'); | ||
960 | |||
961 | if(this.ZoomMode=='fullpage') | ||
962 | this._out('/OpenAction [3 0 R /Fit]'); | ||
963 | else if(this.ZoomMode=='fullwidth') | ||
964 | this._out('/OpenAction [3 0 R /FitH null]'); | ||
965 | else if(this.ZoomMode=='real') | ||
966 | this._out('/OpenAction [3 0 R /XYZ null null 1]'); | ||
967 | else if(!(typeof this.ZoomMode === 'string')) | ||
968 | this._out('/OpenAction [3 0 R /XYZ null null '+sprintf('%.2F',this.ZoomMode/100)+']'); | ||
969 | |||
970 | if(this.LayoutMode=='single') | ||
971 | this._out('/PageLayout /SinglePage'); | ||
972 | else if(this.LayoutMode=='continuous') | ||
973 | this._out('/PageLayout /OneColumn'); | ||
974 | else if(this.LayoutMode=='two') | ||
975 | this._out('/PageLayout /TwoColumnLeft'); | ||
976 | }; | ||
977 | |||
978 | RrdGfxPdf.prototype._enddoc = function() | ||
979 | { | ||
980 | this._out('%PDF-'+this.PDFVersion); | ||
981 | this._putpages(); | ||
982 | this._putresources(); | ||
983 | // Info | ||
984 | this._newobj(); | ||
985 | this._out('<<'); | ||
986 | this._putinfo(); | ||
987 | this._out('>>'); | ||
988 | this._out('endobj'); | ||
989 | // Catalog | ||
990 | this._newobj(); | ||
991 | this._out('<<'); | ||
992 | this._putcatalog(); | ||
993 | this._out('>>'); | ||
994 | this._out('endobj'); | ||
995 | // Cross-ref | ||
996 | var o = this.buffer.length; | ||
997 | this._out('xref'); | ||
998 | this._out('0 '+(this.n+1)); | ||
999 | this._out('0000000000 65535 f '); | ||
1000 | for(var i=1;i<=this.n;i++) | ||
1001 | this._out(sprintf('%010d 00000 n ',this.offsets[i])); | ||
1002 | // Trailer | ||
1003 | this._out('trailer'); | ||
1004 | this._out('<<'); | ||
1005 | this._out('/Size '+(this.n+1)); | ||
1006 | this._out('/Root '+this.n+' 0 R'); | ||
1007 | this._out('/Info '+(this.n-1)+' 0 R'); | ||
1008 | this._out('>>'); | ||
1009 | this._out('startxref'); | ||
1010 | this._out(o); | ||
1011 | this._out('%%EOF'); | ||
1012 | this.state = 3; | ||
1013 | }; | ||
1014 | |||
diff --git a/js/RrdGfxSvg.js b/js/RrdGfxSvg.js new file mode 100644 index 0000000..2bdc1b9 --- /dev/null +++ b/js/RrdGfxSvg.js | |||
@@ -0,0 +1,250 @@ | |||
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 <manuel.luis at gmail.com> | ||
19 | **/ | ||
20 | |||
21 | "use strict"; | ||
22 | |||
23 | /** | ||
24 | * RrdGfxSvg | ||
25 | * @constructor | ||
26 | */ | ||
27 | var RrdGfxSvg = function(svgId) { | ||
28 | this.svg = document.getElementById(svgId) | ||
29 | this.svgns = "http://www.w3.org/2000/svg"; | ||
30 | this.xmlns = "http://www.w3.org/XML/1998/namespace"; | ||
31 | this.path = null; | ||
32 | this.path_color = null; | ||
33 | this.path_width = null; | ||
34 | }; | ||
35 | |||
36 | RrdGfxSvg.prototype.size = function (width, height) | ||
37 | { | ||
38 | while(this.svg.lastChild) | ||
39 | this.svg.removeChild(this.svg.lastChild); | ||
40 | |||
41 | this.svg.setAttribute("width", width+"px"); | ||
42 | this.svg.setAttribute("height", height+"px"); | ||
43 | this.svg.setAttribute("viewBox", "0 0 "+width+" "+height); | ||
44 | }; | ||
45 | |||
46 | RrdGfxSvg.prototype.set_dash = function (dashes, n, offset) | ||
47 | { | ||
48 | }; | ||
49 | |||
50 | RrdGfxSvg.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
51 | { | ||
52 | var shape = document.createElementNS(this.svgns, "line"); | ||
53 | |||
54 | X0 = Math.round(X0)+0.5; | ||
55 | Y0 = Math.round(Y0)+0.5; | ||
56 | X1 = Math.round(X1)+0.5; | ||
57 | Y1 = Math.round(Y1)+0.5; | ||
58 | |||
59 | shape.setAttributeNS(null, "x1", X0); | ||
60 | shape.setAttributeNS(null, "y1", Y0); | ||
61 | shape.setAttributeNS(null, "x2", X1); | ||
62 | shape.setAttributeNS(null, "y2", Y1); | ||
63 | shape.setAttributeNS(null, "stroke-width", width); | ||
64 | shape.setAttributeNS(null, "stroke", color); | ||
65 | |||
66 | this.svg.appendChild(shape); | ||
67 | }; | ||
68 | |||
69 | RrdGfxSvg.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
70 | { | ||
71 | var shape = document.createElementNS(this.svgns, "line"); | ||
72 | |||
73 | X0 = Math.round(X0)+0.5; | ||
74 | Y0 = Math.round(Y0)+0.5; | ||
75 | X1 = Math.round(X1)+0.5; | ||
76 | Y1 = Math.round(Y1)+0.5; | ||
77 | |||
78 | shape.setAttributeNS(null, "x1", X0); | ||
79 | shape.setAttributeNS(null, "y1", Y0); | ||
80 | shape.setAttributeNS(null, "x2", X1); | ||
81 | shape.setAttributeNS(null, "y2", Y1); | ||
82 | shape.setAttributeNS(null, "stroke-width", width); | ||
83 | shape.setAttributeNS(null, "stroke", color); | ||
84 | shape.setAttributeNS(null, "stroke-dasharray", dash_on+','+dash_off); | ||
85 | |||
86 | this.svg.appendChild(shape); | ||
87 | }; | ||
88 | |||
89 | RrdGfxSvg.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
90 | { | ||
91 | var shape = document.createElementNS(this.svgns, "rect"); | ||
92 | |||
93 | var rwidth = Math.abs(X1-X0); | ||
94 | var rheight = Math.abs(Y1-Y0); | ||
95 | |||
96 | shape.setAttributeNS(null, "x", Math.round(X0)+0.5); | ||
97 | shape.setAttributeNS(null, "y", Math.round(Y0-rheight)+0.5); | ||
98 | shape.setAttributeNS(null, "width", rwidth); | ||
99 | shape.setAttributeNS(null, "height", rheight); | ||
100 | shape.setAttributeNS(null, "stroke-width", width); | ||
101 | shape.setAttributeNS(null, "stroke", style); | ||
102 | shape.setAttributeNS(null, "fill", "none"); | ||
103 | |||
104 | this.svg.appendChild(shape); | ||
105 | }; | ||
106 | |||
107 | RrdGfxSvg.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
108 | { | ||
109 | X0 = Math.round(X0)+0.5; | ||
110 | Y0 = Math.round(Y0)+0.5; | ||
111 | X1 = Math.round(X1)+0.5; | ||
112 | Y1 = Math.round(Y1)+0.5; | ||
113 | X2 = Math.round(X2)+0.5; | ||
114 | Y2 = Math.round(Y2)+0.5; | ||
115 | |||
116 | this.path_color = color; | ||
117 | this.path = 'M'+X0+','+Y0; | ||
118 | this.path += ' L'+X1+','+Y1; | ||
119 | this.path += ' L'+X2+','+Y2; | ||
120 | }; | ||
121 | |||
122 | RrdGfxSvg.prototype.add_point = function (x, y) | ||
123 | { | ||
124 | x = Math.round(x)+0.5; | ||
125 | y = Math.round(y)+0.5; | ||
126 | |||
127 | this.path += ' L'+x+','+y; | ||
128 | }; | ||
129 | |||
130 | RrdGfxSvg.prototype.close_path = function () | ||
131 | { | ||
132 | var shape = document.createElementNS(this.svgns, "path"); | ||
133 | |||
134 | this.path += ' Z'; | ||
135 | |||
136 | shape.setAttributeNS(null, "d", this.path); | ||
137 | shape.setAttributeNS(null, "fill", this.path_color); | ||
138 | shape.setAttributeNS(null, "stroke", 'none'); | ||
139 | |||
140 | this.svg.appendChild(shape); | ||
141 | }; | ||
142 | |||
143 | RrdGfxSvg.prototype.stroke_begin = function (width, style) | ||
144 | { | ||
145 | this.path_width = width; | ||
146 | this.path_color = style; | ||
147 | this.path = ''; | ||
148 | }; | ||
149 | |||
150 | RrdGfxSvg.prototype.stroke_end = function () | ||
151 | { | ||
152 | var shape = document.createElementNS(this.svgns, "path"); | ||
153 | |||
154 | shape.setAttributeNS(null, "d", this.path); | ||
155 | shape.setAttributeNS(null, "fill", 'none'); | ||
156 | shape.setAttributeNS(null, "stroke", this.path_color); | ||
157 | shape.setAttributeNS(null, "stroke-width", this.path_width); | ||
158 | shape.setAttributeNS(null, "stroke-linecap", 'round'); | ||
159 | shape.setAttributeNS(null, "stroke-linejoin", 'round'); | ||
160 | |||
161 | this.svg.appendChild(shape); | ||
162 | }; | ||
163 | |||
164 | RrdGfxSvg.prototype.moveTo = function (x,y) | ||
165 | { | ||
166 | x = Math.round(x)+0.5; | ||
167 | y = Math.round(y)+0.5; | ||
168 | |||
169 | this.path += ' M'+x+','+y; | ||
170 | }; | ||
171 | |||
172 | RrdGfxSvg.prototype.lineTo = function (x,y) | ||
173 | { | ||
174 | x = Math.round(x)+0.5; | ||
175 | y = Math.round(y)+0.5; | ||
176 | |||
177 | this.path += ' L'+x+','+y; | ||
178 | }; | ||
179 | |||
180 | RrdGfxSvg.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
181 | { | ||
182 | x = Math.round(x); | ||
183 | y = Math.round(y); | ||
184 | |||
185 | var svgtext = document.createElementNS(this.svgns, "text"); | ||
186 | |||
187 | var data = document.createTextNode(text); | ||
188 | |||
189 | svgtext.setAttributeNS(null, "x", x); | ||
190 | svgtext.setAttributeNS(null, "y", y); | ||
191 | svgtext.setAttributeNS(null, "fill", color); | ||
192 | svgtext.setAttributeNS(null, "stroke", "none"); | ||
193 | svgtext.setAttributeNS(null, "font-family", font.font); | ||
194 | svgtext.setAttributeNS(null, "font-size", font.size+"px"); | ||
195 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve"); | ||
196 | |||
197 | angle=-angle; | ||
198 | svgtext.setAttributeNS(null, "transform", 'rotate('+angle+' '+x+','+y+')' ); | ||
199 | |||
200 | switch (h_align) { | ||
201 | case RrdGraph.GFX_H_LEFT: | ||
202 | svgtext.setAttributeNS(null, "text-anchor", 'start'); | ||
203 | break; | ||
204 | case RrdGraph.GFX_H_RIGHT: | ||
205 | svgtext.setAttributeNS(null, "text-anchor", 'end'); | ||
206 | break; | ||
207 | case RrdGraph.GFX_H_CENTER: | ||
208 | svgtext.setAttributeNS(null, "text-anchor", 'middle'); | ||
209 | break; | ||
210 | } | ||
211 | svgtext.appendChild(data); | ||
212 | this.svg.appendChild(svgtext); | ||
213 | |||
214 | var bbox = svgtext.getBBox(); | ||
215 | |||
216 | switch (v_align) { // FIXME | ||
217 | case RrdGraph.GFX_V_TOP: | ||
218 | svgtext.setAttributeNS(null, "y", y+bbox.height/2); | ||
219 | break; | ||
220 | case RrdGraph.GFX_V_BOTTOM: | ||
221 | svgtext.setAttributeNS(null, "y", y-bbox.height/6); | ||
222 | break; | ||
223 | case RrdGraph.GFX_V_CENTER: | ||
224 | svgtext.setAttributeNS(null, "y", y+bbox.height/4); | ||
225 | break; | ||
226 | } | ||
227 | }; | ||
228 | |||
229 | RrdGfxSvg.prototype.get_text_width = function(start, font, tabwidth, text) | ||
230 | { | ||
231 | var svgtext = document.createElementNS(this.svgns, "text"); | ||
232 | var data = document.createTextNode(text); | ||
233 | svgtext.setAttributeNS(null, "x", 0); | ||
234 | svgtext.setAttributeNS(null, "y", 0); | ||
235 | svgtext.setAttributeNS(null, "fill", 'none'); | ||
236 | svgtext.setAttributeNS(null, "stroke", 'none'); | ||
237 | svgtext.setAttributeNS(null, "font-family", font.font); | ||
238 | svgtext.setAttributeNS(null, "font-size", font.size+"px"); | ||
239 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve"); | ||
240 | svgtext.appendChild(data); | ||
241 | this.svg.appendChild(svgtext); | ||
242 | |||
243 | var bbox = svgtext.getBBox(); | ||
244 | |||
245 | svgtext.removeChild(data); | ||
246 | this.svg.removeChild(svgtext); | ||
247 | |||
248 | return bbox.width; | ||
249 | }; | ||
250 | |||
diff --git a/js/RrdGraph.js b/js/RrdGraph.js new file mode 100644 index 0000000..9b8e3db --- /dev/null +++ b/js/RrdGraph.js | |||
@@ -0,0 +1,2914 @@ | |||
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 <manuel.luis at gmail.com> | ||
20 | **/ | ||
21 | |||
22 | "use strict"; | ||
23 | |||
24 | /** | ||
25 | * RrdGraphDescError | ||
26 | * @constructor | ||
27 | */ | ||
28 | var RrdGraphDescError = function (message) | ||
29 | { | ||
30 | this.prototype = Error.prototype; | ||
31 | this.name = "RrdGraphDescError"; | ||
32 | this.message = (message) ? message : "Error"; | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * RrdGraphDesc | ||
37 | * @constructor | ||
38 | */ | ||
39 | var RrdGraphDesc = function (graph) | ||
40 | { | ||
41 | this.gf = null; /* graphing function */ | ||
42 | this.stack = false; /* boolean */ | ||
43 | this.debug = false; /* boolean */ | ||
44 | this.vname = null; /* name of the variable */ | ||
45 | this.vidx = Number.NaN; /* gdes reference */ | ||
46 | this.rrd = null; /* name of the rrd_file containing data */ | ||
47 | this.ds_nam = null; /* data source name */ | ||
48 | this.ds = -1; /* data source number */ | ||
49 | this.cf = RrdGraphDesc.CF_AVERAGE; /* consolidation function */ | ||
50 | this.cf_reduce = RrdGraphDesc.CF_AVERAGE; /* consolidation function for reduce_data() */ | ||
51 | this.col = null; /* graph color */ | ||
52 | this.format = null; /* format for PRINT AND GPRINT */ | ||
53 | this.legend = null; /* legend */ | ||
54 | this.strftm = false; /* should the VDEF legend be formated with strftime */ | ||
55 | this.leg_x = 0; /* location of legend */ | ||
56 | this.leg_y = 0; | ||
57 | this.yrule = Number.NaN; /* value for y rule line and for VDEF */ | ||
58 | this.xrule = 0; /* time for x rule line and for VDEF */ | ||
59 | this.vf = null; /* instruction for VDEF function */ | ||
60 | this.rpnp = null; /* instructions for CDEF function */ | ||
61 | |||
62 | /* SHIFT implementation */ | ||
63 | this.shidx = 0; /* gdes reference for offset (-1 --> constant) */ | ||
64 | this.shval = 0; /* offset if shidx is -1 */ | ||
65 | this.shift = 0; /* current shift applied */ | ||
66 | |||
67 | /* description of data fetched for the graph element */ | ||
68 | this.start = graph.start; /* timestaps for first and last data element */ | ||
69 | this.end = graph.end; | ||
70 | this.start_orig = graph.start; /* timestaps for first and last data element */ | ||
71 | this.end_orig = graph.end; | ||
72 | this.step = graph.step; /* time between samples */ | ||
73 | this.step_orig = graph.step; /* time between samples */ | ||
74 | this.ds_cnt = 0; /* how many data sources are there in the fetch */ | ||
75 | this.data_first = 0; /* first pointer to this data */ | ||
76 | this.ds_namv = []; /* name of datasources in the fetch. */ | ||
77 | this.data = []; /* the raw data drawn from the rrd */ | ||
78 | this.p_data = []; /* processed data, xsize elments */ | ||
79 | this.linewidth = 0; /* linewideth */ | ||
80 | |||
81 | /* dashed line stuff */ | ||
82 | this.dash = false; /* boolean, draw dashed line? */ | ||
83 | this.p_dashes = []; /* pointer do dash array which keeps the lengths of dashes */ | ||
84 | this.ndash = false; /* number of dash segments */ | ||
85 | this.offset = 0; /* dash offset along the line */ | ||
86 | |||
87 | this.txtalign = 0; /* change default alignment strategy for text */ | ||
88 | |||
89 | /** ** **/ | ||
90 | var args = []; // FIXME other way ¿? | ||
91 | var type = arguments[1] | ||
92 | args[0] = arguments[0]; | ||
93 | for(var i = 2; i < arguments.length; i++) args[i-1] = arguments[i]; | ||
94 | |||
95 | switch (type) { | ||
96 | case RrdGraphDesc.GF_GPRINT: | ||
97 | this.gprint.apply(this, args); | ||
98 | break; | ||
99 | case RrdGraphDesc.GF_COMMENT: | ||
100 | this.comment.apply(this, args); | ||
101 | break; | ||
102 | case RrdGraphDesc.GF_HRULE: | ||
103 | this.hrule.apply(this, args); | ||
104 | break; | ||
105 | case RrdGraphDesc.GF_VRULE: | ||
106 | this.vrule.apply(this, args); | ||
107 | break; | ||
108 | case RrdGraphDesc.GF_LINE: | ||
109 | this.line.apply(this, args); | ||
110 | break; | ||
111 | case RrdGraphDesc.GF_AREA: | ||
112 | this.area.apply(this, args); | ||
113 | break; | ||
114 | case RrdGraphDesc.GF_TICK: | ||
115 | this.tick.apply(this, args); | ||
116 | break; | ||
117 | case RrdGraphDesc.GF_TEXTALIGN: | ||
118 | this.textaling.apply(this, args); | ||
119 | break; | ||
120 | case RrdGraphDesc.GF_DEF: | ||
121 | this.def.apply(this, args); | ||
122 | break; | ||
123 | case RrdGraphDesc.GF_CDEF: | ||
124 | this.cdef.apply(this, args); | ||
125 | break; | ||
126 | case RrdGraphDesc.GF_VDEF: | ||
127 | this.vdef.apply(this, args); | ||
128 | break; | ||
129 | case RrdGraphDesc.GF_SHIFT: | ||
130 | this.fshift.apply(this, args); | ||
131 | break; | ||
132 | } | ||
133 | }; | ||
134 | |||
135 | RrdGraphDesc.GF_PRINT = 0; | ||
136 | RrdGraphDesc.GF_GPRINT = 1; | ||
137 | RrdGraphDesc.GF_COMMENT = 2; | ||
138 | RrdGraphDesc.GF_HRULE = 3; | ||
139 | RrdGraphDesc.GF_VRULE = 4; | ||
140 | RrdGraphDesc.GF_LINE = 5; | ||
141 | RrdGraphDesc.GF_AREA = 6; | ||
142 | RrdGraphDesc.GF_STACK = 7; | ||
143 | RrdGraphDesc.GF_TICK = 8; | ||
144 | RrdGraphDesc.GF_TEXTALIGN = 9; | ||
145 | RrdGraphDesc.GF_DEF = 10; | ||
146 | RrdGraphDesc.GF_CDEF = 11; | ||
147 | RrdGraphDesc.GF_VDEF = 12; | ||
148 | RrdGraphDesc.GF_SHIFT = 13; | ||
149 | RrdGraphDesc.GF_XPORT = 14; | ||
150 | |||
151 | RrdGraphDesc.CF_AVERAGE = 0; | ||
152 | RrdGraphDesc.CF_MINIMUM = 1; | ||
153 | RrdGraphDesc.CF_MAXIMUM = 2; | ||
154 | RrdGraphDesc.CF_LAST = 3; | ||
155 | RrdGraphDesc.CF_HWPREDICT = 4; | ||
156 | RrdGraphDesc.CF_SEASONAL = 5; | ||
157 | RrdGraphDesc.CF_DEVPREDICT = 6; | ||
158 | RrdGraphDesc.CF_DEVSEASONAL = 7; | ||
159 | RrdGraphDesc.CF_FAILURES = 8; | ||
160 | RrdGraphDesc.CF_MHWPREDICT = 9; | ||
161 | |||
162 | RrdGraphDesc.TXA_LEFT = 0; | ||
163 | RrdGraphDesc.TXA_RIGHT = 1; | ||
164 | RrdGraphDesc.TXA_CENTER = 2; | ||
165 | RrdGraphDesc.TXA_JUSTIFIED = 3; | ||
166 | |||
167 | RrdGraphDesc.cf_conv = function (str) | ||
168 | { | ||
169 | switch (str){ | ||
170 | case 'AVERAGE': return RrdGraphDesc.CF_AVERAGE; | ||
171 | case 'MIN': return RrdGraphDesc.CF_MINIMUM; | ||
172 | case 'MAX': return RrdGraphDesc.CF_MAXIMUM; | ||
173 | case 'LAST': return RrdGraphDesc.CF_LAST; | ||
174 | case 'HWPREDICT': return RrdGraphDesc.CF_HWPREDICT; | ||
175 | case 'MHWPREDICT': return RrdGraphDesc.CF_MHWPREDICT; | ||
176 | case 'DEVPREDICT': return RrdGraphDesc.CF_DEVPREDICT; | ||
177 | case 'SEASONAL': return RrdGraphDesc.CF_SEASONAL; | ||
178 | case 'DEVSEASONAL': return RrdGraphDesc.CF_DEVSEASONAL; | ||
179 | case 'FAILURES': return RrdGraphDesc.CF_FAILURES; | ||
180 | } | ||
181 | return -1; | ||
182 | }; | ||
183 | |||
184 | RrdGraphDesc.cf2str = function (cf) | ||
185 | { | ||
186 | switch (cf){ | ||
187 | case RrdGraphDesc.CF_AVERAGE: return 'AVERAGE'; | ||
188 | case RrdGraphDesc.CF_MINIMUM: return 'MIN'; | ||
189 | case RrdGraphDesc.CF_MAXIMUM: return 'MAX'; | ||
190 | case RrdGraphDesc.CF_LAST: return 'LAST'; | ||
191 | case RrdGraphDesc.CF_HWPREDICT: return 'HWPREDICT'; | ||
192 | case RrdGraphDesc.CF_MHWPREDICT: return 'MHWPREDICT'; | ||
193 | case RrdGraphDesc.CF_DEVPREDICT: return 'DEVPREDICT'; | ||
194 | case RrdGraphDesc.CF_SEASONAL: return 'SEASONAL'; | ||
195 | case RrdGraphDesc.CF_DEVSEASONAL: return 'DEVSEASONAL'; | ||
196 | case RrdGraphDesc.CF_FAILURES: return 'FAILURES'; | ||
197 | } | ||
198 | return ''; | ||
199 | }; | ||
200 | |||
201 | RrdGraphDesc.prototype.def = function (graph, vname, rrdfile, name, cf, step, start, end, reduce) | ||
202 | { | ||
203 | var start_t = new RrdTime(this.start); | ||
204 | var end_t = new RrdTime(this.end); | ||
205 | |||
206 | this.gf = RrdGraphDesc.GF_DEF; | ||
207 | this.vname = vname; | ||
208 | this.vidx = graph.find_var(vname); | ||
209 | this.rrd = rrdfile; | ||
210 | this.ds_nam = name; | ||
211 | this.cf = RrdGraphDesc.cf_conv(cf); | ||
212 | |||
213 | if (step != undefined && step != null) | ||
214 | this.step = step; | ||
215 | if (start != undefined && start != null) | ||
216 | start_t = new RrdTime(start); | ||
217 | if (end != undefined && end != null) | ||
218 | end_t = new RrdTime(end); | ||
219 | if (reduce === undefined || reduce === null) | ||
220 | this.cf_reduce = this.cf; // ¿? | ||
221 | else | ||
222 | this.cf_reduce = RrdGraphDesc.cf_conv(reduce); | ||
223 | this.legend = ''; | ||
224 | |||
225 | var start_end = RrdTime.proc_start_end(start_t, end_t); // FIXME here? | ||
226 | this.start = start_end[0]; | ||
227 | this.end = start_end[1]; | ||
228 | this.start_orig = start_end[0]; | ||
229 | this.end_orig = start_end[1]; | ||
230 | }; | ||
231 | |||
232 | RrdGraphDesc.prototype.cdef = function (graph, vname, rpn) | ||
233 | { | ||
234 | this.gf = RrdGraphDesc.GF_CDEF; | ||
235 | this.vname = vname; | ||
236 | this.vidx = graph.find_var(vname); | ||
237 | this.rpnp = new RrdRpn(rpn, graph.gdes); | ||
238 | this.legend = ''; | ||
239 | }; | ||
240 | |||
241 | RrdGraphDesc.prototype.vdef = function (graph, vname, rpn) | ||
242 | { | ||
243 | this.gf = RrdGraphDesc.GF_VDEF; | ||
244 | this.vname = vname; | ||
245 | |||
246 | var index = rpn.indexOf(','); | ||
247 | var name = rpn.substring(0,index); | ||
248 | this.vidx = graph.find_var(name); // FIXME checks | ||
249 | if (graph.gdes[this.vidx].gf != RrdGraphDesc.GF_DEF && graph.gdes[this.vidx].gf != RrdGraphDesc.GF_CDEF) { | ||
250 | throw new RrdGraphDescError('variable "'+name+'" not DEF nor CDEF in VDEF.'); | ||
251 | } | ||
252 | this.vf = new RrdVdef(rpn.substring(index+1)); | ||
253 | this.legend = ''; | ||
254 | }; | ||
255 | |||
256 | RrdGraphDesc.prototype.fshift = function (graph, vname, offset) | ||
257 | { | ||
258 | this.gf = RrdGraphDesc.GF_SHIFT; | ||
259 | this.vname = vname; // ¿? | ||
260 | this.vidx = graph.find_var(vname); // FIXME checks | ||
261 | |||
262 | if (graph.gdes[this.vidx].gf === RrdGraphDesc.GF_VDEF) | ||
263 | throw new RrdGraphDescError("Cannot shift a VDEF: '%s' in line '"+graph.gdes[this.vidx].vname+"'"); | ||
264 | if (graph.gdes[this.vidx].gf !== RrdGraphDesc.GF_DEF && graph.gdes[this.vidx].gf !== RrdGraphDesc.GF_CDEF) | ||
265 | throw new RrdGraphDescError("Encountered unknown type variable '"+graph.gdes[this.vidx].vname+"'"); | ||
266 | |||
267 | this.shidx = graph.find_var(offset); | ||
268 | if (this.shidx >= 0) { | ||
269 | if (graph.gdes[gdp.shidx].gf === RrdGraphDesc.GF_DEF || graph.gdes[gdp.shidx].gf === RrdGraphDesc.GF_CDEF) | ||
270 | throw new RrdGraphDescError("Offset cannot be a (C)DEF: '"+graph.gdes[gdp.shidx].gf+"'"); | ||
271 | if (graph.gdes[gdp.shidx].gf !== RrdGraphDesc.GF_VDEF) | ||
272 | throw new RrdGraphDescError("Encountered unknown type variable '"+graph.gdes[gdp.shidx].vname+"'"); | ||
273 | } else { | ||
274 | this.shval = parseInt(offset, 10); // FIXME check | ||
275 | this.shidx = -1; | ||
276 | } | ||
277 | this.legend = ''; | ||
278 | }; | ||
279 | |||
280 | RrdGraphDesc.prototype.line = function (graph, width, value, color, legend, stack) | ||
281 | { | ||
282 | this.gf = RrdGraphDesc.GF_LINE; | ||
283 | this.vname = value; | ||
284 | this.vidx = graph.find_var(value); | ||
285 | this.linewidth = width; | ||
286 | this.col = color; | ||
287 | if (legend === undefined) this.legend = ''; | ||
288 | else this.legend = ' '+legend; | ||
289 | if (stack === undefined) this.stack = false; | ||
290 | else this.stack = stack; | ||
291 | this.format = this.legend; | ||
292 | }; | ||
293 | |||
294 | RrdGraphDesc.prototype.area = function (graph, value, color, legend, stack) | ||
295 | { | ||
296 | this.gf = RrdGraphDesc.GF_AREA; | ||
297 | this.vname = value; | ||
298 | this.vidx = graph.find_var(value); | ||
299 | this.col = color; | ||
300 | if (legend === undefined) this.legend = ''; | ||
301 | else this.legend = ' '+legend; | ||
302 | if (stack === undefined) this.stack = false; | ||
303 | else this.stack = stack; | ||
304 | this.format = this.legend; | ||
305 | }; | ||
306 | |||
307 | RrdGraphDesc.prototype.tick = function (graph, vname, color, fraction, legend) | ||
308 | { | ||
309 | this.gf = RrdGraphDesc.GF_TICK; | ||
310 | this.vname = vname; | ||
311 | this.vidx = graph.find_var(vname); | ||
312 | this.col = color; | ||
313 | if (fraction !== undefined) | ||
314 | this.yrule = fraction; | ||
315 | if (legend === undefined) this.legend = ''; | ||
316 | else this.legend = ' '+legend; | ||
317 | this.format = this.legend; | ||
318 | }; | ||
319 | |||
320 | RrdGraphDesc.prototype.gprint = function (graph, vname, cf, format, strftimefmt) | ||
321 | { | ||
322 | this.gf = RrdGraphDesc.GF_GPRINT; | ||
323 | this.vname = vname; | ||
324 | this.vidx = graph.find_var(vname); | ||
325 | this.legend = ''; | ||
326 | if (format === undefined) { | ||
327 | this.format = cf; | ||
328 | switch (graph.gdes[this.vidx].gf) { | ||
329 | case RrdGraphDesc.GF_DEF: | ||
330 | case RrdGraphDesc.GF_CDEF: | ||
331 | this.cf = graph.gdes[this.vidx].cf; | ||
332 | break; | ||
333 | case RrdGraphDesc.GF_VDEF: | ||
334 | break; | ||
335 | default: | ||
336 | throw new RrdGraphDescError("Encountered unknown type variable "+graph.gdes[this.vidx].vname); | ||
337 | } | ||
338 | } else { | ||
339 | this.cf = RrdGraphDesc.cf_conv(cf); | ||
340 | this.format = format; | ||
341 | } | ||
342 | if (graph.gdes[this.vidx].gf === RrdGraphDesc.GF_VDEF && strftimefmt === true) | ||
343 | this.strftm = true; | ||
344 | }; | ||
345 | |||
346 | RrdGraphDesc.prototype.comment = function (graph, text) | ||
347 | { | ||
348 | this.gf = RrdGraphDesc.GF_COMMENT; | ||
349 | this.vidx = -1; | ||
350 | this.legend = text; | ||
351 | }; | ||
352 | |||
353 | RrdGraphDesc.prototype.textalign = function (graph, align) | ||
354 | { | ||
355 | this.gf = RrdGraphDesc.GF_TEXTALIGN; | ||
356 | this.vidx = -1; | ||
357 | if (align === "left") { | ||
358 | this.txtalign = RrdGraphDesc.TXA_LEFT; | ||
359 | } else if (align === "right") { | ||
360 | this.txtalign = RrdGraphDesc.TXA_RIGHT; | ||
361 | } else if (align === "justified") { | ||
362 | this.txtalign = RrdGraphDesc.TXA_JUSTIFIED; | ||
363 | } else if (align === "center") { | ||
364 | this.txtalign = RrdGraphDesc.TXA_CENTER; | ||
365 | } else { | ||
366 | throw new RrdGraphDescError("Unknown alignement type '"+align+"'"); | ||
367 | } | ||
368 | }; | ||
369 | |||
370 | RrdGraphDesc.prototype.vrule = function (graph, time, color, legend) | ||
371 | { | ||
372 | this.gf = RrdGraphDesc.GF_VRULE; | ||
373 | this.xrule = time; | ||
374 | this.col = color; | ||
375 | if (legend === undefined) this.legend = ''; | ||
376 | else this.legend = ' '+legend; | ||
377 | }; | ||
378 | |||
379 | RrdGraphDesc.prototype.hrule = function (graph, value, color, legend) | ||
380 | { | ||
381 | this.gf = RrdGraphDesc.GF_HRULE; | ||
382 | this.yrule = value; | ||
383 | this.col = color; | ||
384 | if (legend === undefined) this.legend = ''; | ||
385 | else this.legend = ' '+legend; | ||
386 | }; | ||
387 | |||
388 | /** | ||
389 | * RrdVdefError | ||
390 | * @constructor | ||
391 | */ | ||
392 | var RrdVdefError = function (message) | ||
393 | { | ||
394 | this.prototype = Error.prototype; | ||
395 | this.name = "RrdVdefError"; | ||
396 | this.message = (message) ? message : "Error"; | ||
397 | }; | ||
398 | |||
399 | /** | ||
400 | * RrdVdef | ||
401 | * @constructor | ||
402 | */ | ||
403 | var RrdVdef = function(vname, str) /* parse */ | ||
404 | { | ||
405 | var param; | ||
406 | var func; | ||
407 | var n = 0; | ||
408 | |||
409 | this.expr = str; | ||
410 | this.op = null; | ||
411 | this.param = null; | ||
412 | this.val = null; | ||
413 | this.when = null; | ||
414 | |||
415 | var index = str.indexOf(','); | ||
416 | if (index != -1) { | ||
417 | param = parseFloat(str.substr(0,index)); | ||
418 | func = str.substr(index+1); | ||
419 | } else { | ||
420 | param = Number.NaN; | ||
421 | func = str; | ||
422 | } | ||
423 | |||
424 | if (func === 'PERCENT') this.op = RrdVdef.VDEF_PERCENT; | ||
425 | else if (func === 'PERCENTNAN') this.op = RrdVdef.VDEF_PERCENTNAN; | ||
426 | else if (func === 'MAXIMUM') this.op = RrdVdef.VDEF_MAXIMUM; | ||
427 | else if (func === 'AVERAGE') this.op = RrdVdef.VDEF_AVERAGE; | ||
428 | else if (func === 'STDEV') this.op = RrdVdef.VDEF_STDEV; | ||
429 | else if (func === 'MINIMUM') this.op = RrdVdef.VDEF_MINIMUM; | ||
430 | else if (func === 'TOTAL') this.op = RrdVdef.VDEF_TOTAL; | ||
431 | else if (func === 'FIRST') this.op = RrdVdef.VDEF_FIRST; | ||
432 | else if (func === 'LAST') this.op = RrdVdef.VDEF_LAST; | ||
433 | else if (func === 'LSLSLOPE') this.op = RrdVdef.VDEF_LSLSLOPE; | ||
434 | else if (func === 'LSLINT') this.op = RrdVdef.VDEF_LSLINT; | ||
435 | else if (func === 'LSLCORREL') this.op = RrdVdef.VDEF_LSLCORREL; | ||
436 | else { | ||
437 | throw new RrdVdefError('Unknown function "'+func+'" in VDEF "'+vame+'"'); | ||
438 | } | ||
439 | |||
440 | switch (this.op) { | ||
441 | case RrdVdef.VDEF_PERCENT: | ||
442 | case RrdVdef.VDEF_PERCENTNAN: | ||
443 | if (isNaN(param)) { /* no parameter given */ | ||
444 | throw new RrdVdefError("Function '"+func+"' needs parameter in VDEF '"+vname+"'"); | ||
445 | } | ||
446 | if (param >= 0.0 && param <= 100.0) { | ||
447 | this.param = param; | ||
448 | this.val = Number.NaN; /* undefined */ | ||
449 | this.when = 0; /* undefined */ | ||
450 | } else { | ||
451 | throw new RrdVdefError("Parameter '"+param+"' out of range in VDEF '"+vname+"'"); | ||
452 | } | ||
453 | break; | ||
454 | case RrdVdef.VDEF_MAXIMUM: | ||
455 | case RrdVdef.VDEF_AVERAGE: | ||
456 | case RrdVdef.VDEF_STDEV: | ||
457 | case RrdVdef.VDEF_MINIMUM: | ||
458 | case RrdVdef.VDEF_TOTAL: | ||
459 | case RrdVdef.VDEF_FIRST: | ||
460 | case RrdVdef.VDEF_LAST: | ||
461 | case RrdVdef.VDEF_LSLSLOPE: | ||
462 | case RrdVdef.VDEF_LSLINT: | ||
463 | case RrdVdef.VDEF_LSLCORREL: | ||
464 | if (isNaN(param)) { | ||
465 | this.param = Number.NaN; | ||
466 | this.val = Number.NaN; | ||
467 | this.when = 0; | ||
468 | } else { | ||
469 | throw new RrdVdefError("Function '"+func+"' needs no parameter in VDEF '"+vname+"'"); | ||
470 | } | ||
471 | break; | ||
472 | } | ||
473 | }; | ||
474 | |||
475 | RrdVdef.VDEF_MAXIMUM = 0; | ||
476 | RrdVdef.VDEF_MINIMUM = 1; | ||
477 | RrdVdef.VDEF_AVERAGE = 2; | ||
478 | RrdVdef.VDEF_STDEV = 3; | ||
479 | RrdVdef.VDEF_PERCENT = 4; | ||
480 | RrdVdef.VDEF_TOTAL = 5; | ||
481 | RrdVdef.VDEF_FIRST = 6; | ||
482 | RrdVdef.VDEF_LAST = 7; | ||
483 | RrdVdef.VDEF_LSLSLOPE = 8; | ||
484 | RrdVdef.VDEF_LSLINT = 9; | ||
485 | RrdVdef.VDEF_LSLCORREL = 10; | ||
486 | RrdVdef.VDEF_PERCENTNAN = 11; | ||
487 | |||
488 | RrdVdef.prototype.vdef_percent_compar = function (a, b) | ||
489 | { /* Equality is not returned; this doesn't hurt except (maybe) for a little performance. */ | ||
490 | /* NaN < -INF < finite_values < INF */ | ||
491 | if (isNaN(a)) return -1; | ||
492 | if (isNaN(b)) return 1; | ||
493 | /* NaN doesn't reach this part so INF and -INF are extremes. The sign from isinf() is compatible with the sign we return */ | ||
494 | if (!isFinite(a)) { | ||
495 | if (a === -Infinity) return -1; | ||
496 | else return 1; | ||
497 | } | ||
498 | if (!isFinite(b)) { | ||
499 | if (b === -Infinity) return -1; | ||
500 | else return 1; | ||
501 | } | ||
502 | /* If we reach this, both values must be finite */ | ||
503 | if (a < b) return -1; | ||
504 | else return 1; | ||
505 | }; | ||
506 | |||
507 | RrdVdef.prototype.calc = function(src) | ||
508 | { | ||
509 | var data; | ||
510 | var step, steps; | ||
511 | |||
512 | data = src.data; | ||
513 | steps = (src.end - src.start) / src.step; | ||
514 | |||
515 | switch (this.op) { | ||
516 | case RrdVdef.VDEF_PERCENT: | ||
517 | var array = []; | ||
518 | var field; | ||
519 | |||
520 | for (step = 0; step < steps; step++) { | ||
521 | array[step] = data[step * src.ds_cnt]; | ||
522 | } | ||
523 | array.sort(this.vdef_percent_compar); | ||
524 | field = Math.round((this.param * (steps - 1)) / 100.0); | ||
525 | this.val = array[field]; | ||
526 | this.when = 0; /* no time component */ | ||
527 | break; | ||
528 | case RrdVdef.VDEF_PERCENTNAN: | ||
529 | var array = []; | ||
530 | var field; | ||
531 | |||
532 | field=0; | ||
533 | for (step = 0; step < steps; step++) { | ||
534 | if (!isNaN(data[step * src.ds_cnt])) { | ||
535 | array[field] = data[step * src.ds_cnt]; | ||
536 | } | ||
537 | } | ||
538 | array.sort(vdef_percent_compar); | ||
539 | field = Math.round(this.param * (field - 1) / 100.0); | ||
540 | his.val = array[field]; | ||
541 | this.when = 0; /* no time component */ | ||
542 | break; | ||
543 | case RrdVdef.VDEF_MAXIMUM: | ||
544 | step = 0; | ||
545 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
546 | if (step === steps) { | ||
547 | this.val = Number.NaN; | ||
548 | this.when = 0; | ||
549 | } else { | ||
550 | this.val = data[step * src.ds_cnt]; | ||
551 | this.when = src.start + (step + 1) * src.step; | ||
552 | } | ||
553 | while (step != steps) { | ||
554 | if (isFinite(data[step * src.ds_cnt])) { | ||
555 | if (data[step * src.ds_cnt] > this.val) { | ||
556 | this.val = data[step * src.ds_cnt]; | ||
557 | this.when = src.start + (step + 1) * src.step; | ||
558 | } | ||
559 | } | ||
560 | step++; | ||
561 | } | ||
562 | break; | ||
563 | case RrdVdef.VDEF_TOTAL: | ||
564 | case RrdVdef.VDEF_STDEV: | ||
565 | case RrdVdef.VDEF_AVERAGE: | ||
566 | var cnt = 0; | ||
567 | var sum = 0.0; | ||
568 | var average = 0.0; | ||
569 | |||
570 | for (step = 0; step < steps; step++) { | ||
571 | if (isFinite(data[step * src.ds_cnt])) { | ||
572 | sum += data[step * src.ds_cnt]; | ||
573 | cnt++; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | if (cnt) { | ||
578 | if (this.op === RrdVdef.VDEF_TOTAL) { | ||
579 | this.val = sum * src.step; | ||
580 | this.when = 0; /* no time component */ | ||
581 | } else if (this.op === RrdVdef.VDEF_AVERAGE) { | ||
582 | this.val = sum / cnt; | ||
583 | this.when = 0; /* no time component */ | ||
584 | } else { | ||
585 | average = sum / cnt; | ||
586 | sum = 0.0; | ||
587 | for (step = 0; step < steps; step++) { | ||
588 | if (isFinite(data[step * src.ds_cnt])) { | ||
589 | sum += Math.pow((data[step * src.ds_cnt] - average), 2.0); | ||
590 | } | ||
591 | } | ||
592 | this.val = Math.pow(sum / cnt, 0.5); | ||
593 | this.when = 0; /* no time component */ | ||
594 | } | ||
595 | } else { | ||
596 | this.val = Number.NaN; | ||
597 | this.when = 0; | ||
598 | } | ||
599 | break; | ||
600 | case RrdVdef.VDEF_MINIMUM: | ||
601 | step = 0; | ||
602 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
603 | if (step === steps) { | ||
604 | this.val = Number.NaN; | ||
605 | this.when = 0; | ||
606 | } else { | ||
607 | this.val = data[step * src.ds_cnt]; | ||
608 | this.when = src.start + (step + 1) * src.step; | ||
609 | } | ||
610 | while (step != steps) { | ||
611 | if (isFinite(data[step * src.ds_cnt])) { | ||
612 | if (data[step * src.ds_cnt] < this.val) { | ||
613 | this.val = data[step * src.ds_cnt]; | ||
614 | this.when = src.start + (step + 1) * src.step; | ||
615 | } | ||
616 | } | ||
617 | step++; | ||
618 | } | ||
619 | break; | ||
620 | case RrdVdef.VDEF_FIRST: | ||
621 | step = 0; | ||
622 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
623 | if (step === steps) { /* all entries were NaN */ | ||
624 | this.val = Number.NaN; | ||
625 | this.when = 0; | ||
626 | } else { | ||
627 | this.val = data[step * src.ds_cnt]; | ||
628 | this.when = src.start + step * src.step; | ||
629 | } | ||
630 | break; | ||
631 | case RrdVdef.VDEF_LAST: | ||
632 | step = steps - 1; | ||
633 | while (step >= 0 && isNaN(data[step * src.ds_cnt])) step--; | ||
634 | if (step < 0) { /* all entries were NaN */ | ||
635 | this.val = Number.NaN; | ||
636 | this.when = 0; | ||
637 | } else { | ||
638 | this.val = data[step * src.ds_cnt]; | ||
639 | this.when = src.start + (step + 1) * src.step; | ||
640 | } | ||
641 | break; | ||
642 | case RrdVdef.VDEF_LSLSLOPE: | ||
643 | case RrdVdef.VDEF_LSLINT: | ||
644 | case RrdVdef.VDEF_LSLCORREL: | ||
645 | var cnt = 0; | ||
646 | var SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl; | ||
647 | |||
648 | SUMx = 0; | ||
649 | SUMy = 0; | ||
650 | SUMxy = 0; | ||
651 | SUMxx = 0; | ||
652 | SUMyy = 0; | ||
653 | |||
654 | for (step = 0; step < steps; step++) { | ||
655 | if (isFinite(data[step * src.ds_cnt])) { | ||
656 | cnt++; | ||
657 | SUMx += step; | ||
658 | SUMxx += step * step; | ||
659 | SUMxy += step * data[step * src.ds_cnt]; | ||
660 | SUMy += data[step * src.ds_cnt]; | ||
661 | SUMyy += data[step * src.ds_cnt] * data[step * src.ds_cnt]; | ||
662 | } | ||
663 | } | ||
664 | |||
665 | slope = (SUMx * SUMy - cnt * SUMxy) / (SUMx * SUMx - cnt * SUMxx); | ||
666 | y_intercept = (SUMy - slope * SUMx) / cnt; | ||
667 | correl = (SUMxy - (SUMx * SUMy) / cnt) / Math.sqrt((SUMxx - (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt)); | ||
668 | |||
669 | if (cnt) { | ||
670 | if (this.op === RrdVdef.VDEF_LSLSLOPE) { | ||
671 | this.val = slope; | ||
672 | this.when = 0; | ||
673 | } else if (this.op === RrdVdef.VDEF_LSLINT) { | ||
674 | this.val = y_intercept; | ||
675 | this.when = 0; | ||
676 | } else if (this.op === RrdVdef.VDEF_LSLCORREL) { | ||
677 | this.val = correl; | ||
678 | this.when = 0; | ||
679 | } | ||
680 | } else { | ||
681 | this.val = Number.NaN; | ||
682 | this.when = 0; | ||
683 | } | ||
684 | break; | ||
685 | } | ||
686 | return 0; | ||
687 | }; | ||
688 | |||
689 | /** | ||
690 | * RrdGraphError | ||
691 | * @constructor | ||
692 | */ | ||
693 | var RrdGraphError = function (message) | ||
694 | { | ||
695 | this.prototype = Error.prototype; | ||
696 | this.name = "RrdGraphError"; | ||
697 | this.message = (message) ? message : "Error"; | ||
698 | }; | ||
699 | |||
700 | /** | ||
701 | * RrdGraph | ||
702 | * @constructor | ||
703 | */ | ||
704 | var RrdGraph = function (gfx, data) | ||
705 | { | ||
706 | this.gfx = gfx; /* graphics object */ | ||
707 | this.data = data; /* fetch data object */ | ||
708 | |||
709 | this.minval = Number.NaN; /* extreme values in the data */ | ||
710 | this.maxval = Number.NaN; | ||
711 | /* status information */ | ||
712 | //with_markup: 0, | ||
713 | this.xorigin = 0; /* where is (0,0) of the graph */ | ||
714 | this.yorigin = 0; | ||
715 | this.xOriginTitle = 0; /* where is the origin of the title */ | ||
716 | this.yOriginTitle = 0; | ||
717 | this.xOriginLegendY = 0; /* where is the origin of the y legend */ | ||
718 | this.yOriginLegendY = 0; | ||
719 | this.xOriginLegendY2 = 0; /* where is the origin of the second y legend */ | ||
720 | this.yOriginLegendY2 = 0; | ||
721 | this.xOriginLegend = 0; /* where is the origin of the legend */ | ||
722 | this.yOriginLegend = 0; | ||
723 | this.ximg = 0; /* total size of the image */ | ||
724 | this.yimg = 0; | ||
725 | this.legendwidth = 0; /* the calculated height and width of the legend */ | ||
726 | this.legendheight = 0; | ||
727 | this.magfact = 1; /* numerical magnitude */ | ||
728 | this.symbol = null; /* magnitude symbol for y-axis */ | ||
729 | this.viewfactor = 1.0; /* how should the numbers on the y-axis be scaled for viewing ? */ | ||
730 | |||
731 | this.base = 1000; /* 1000 or 1024 depending on what we graph */ | ||
732 | |||
733 | this.start = 0; /* what time does the graph cover */ | ||
734 | this.end = 0; | ||
735 | |||
736 | this.xlab_form = null; /* format for the label on the xaxis */ | ||
737 | /* public */ | ||
738 | this.xsize = 400; /* graph area size in pixels */ | ||
739 | this.ysize = 100; | ||
740 | this.zoom = 1; | ||
741 | this.grid_dash_on = 1; | ||
742 | this.grid_dash_off = 1; | ||
743 | this.second_axis_scale = 0; /* relative to the first axis (0 to disable) */ | ||
744 | this.second_axis_shift = 0; /* how much is it shifted vs the first axis */ | ||
745 | this.second_axis_legend = null; /* label to put on the seond axis */ | ||
746 | this.second_axis_format = null; /* format for the numbers on the scond axis */ | ||
747 | this.draw_x_grid = true; /* no x-grid at all */ | ||
748 | this.draw_y_grid = true; /* no y-grid at all */ | ||
749 | this.ygridstep = Number.NaN; /* user defined step for y grid */ | ||
750 | this.ylabfact = 0; /* every how many y grid shall a label be written ? */ | ||
751 | this.draw_3d_border = 2; /* size of border in pixels, 0 for off */ | ||
752 | this.dynamic_labels = false;/* pick the label shape according to the line drawn */ | ||
753 | this.ylegend = null; /* legend along the yaxis */ | ||
754 | this.title = ''; /* title for graph */ | ||
755 | this.watermark = null; /* watermark for graph */ | ||
756 | this.tabwidth = 40; /* tabwdith */ | ||
757 | this.step = 0; /* any preference for the default step ? */ | ||
758 | this.setminval = Number.NaN; /* extreme values in the data */ | ||
759 | this.setmaxval = Number.NaN; | ||
760 | this.rigid = false; /* do not expand range even with values outside */ | ||
761 | this.gridfit = true; /* adjust y-axis range etc so all grindlines falls in integer pixel values */ | ||
762 | this.lazy = 0; /* only update the image if there is reasonable probablility that the existing one is out of date */ | ||
763 | this.legendposition = RrdGraph.LEGEND_POS_SOUTH; /* the position of the legend: north, west, south or east */ | ||
764 | this.legenddirection = RrdGraph.LEGEND_DIR_TOP_DOWN; /* The direction of the legend topdown or bottomup */ | ||
765 | this.logarithmic = false; /* scale the yaxis logarithmic */ | ||
766 | this.force_scale_min = 0; /* Force a scale--min */ | ||
767 | this.force_scale_max = 0; /* Force a scale--max */ | ||
768 | this.unitsexponent = 9999; /* 10*exponent for units on y-asis */ | ||
769 | this.unitslength = 6; /* width of the yaxis labels */ | ||
770 | this.forceleftspace = false; /* do not kill the space to the left of the y-axis if there is no grid */ | ||
771 | this.slopemode = false; /* connect the dots of the curve directly, not using a stair */ | ||
772 | this.alt_ygrid = false; /* use alternative y grid algorithm */ | ||
773 | this.alt_autoscale = false; /* use alternative algorithm to find lower and upper bounds */ | ||
774 | this.alt_autoscale_min = false; /* use alternative algorithm to find lower bounds */ | ||
775 | this.alt_autoscale_max = false; /* use alternative algorithm to find upper bounds */ | ||
776 | this.no_legend = false; /* use no legend */ | ||
777 | this.no_minor = false; /* Turn off minor gridlines */ | ||
778 | this.only_graph = false; /* use only graph */ | ||
779 | this.force_rules_legend = false; /* force printing of HRULE and VRULE legend */ | ||
780 | this.force_units = false; /* mask for all FORCE_UNITS_* flags */ | ||
781 | this.force_units_si = false; /* force use of SI units in Y axis (no effect in linear graph, SI instead of E in log graph) */ | ||
782 | this.full_size_mode = false; /* -width and -height indicate the total size of the image */ | ||
783 | this.no_rrdtool_tag = false; /* disable the rrdtool tag */ | ||
784 | |||
785 | this.xlab_user = null; | ||
786 | this.ygrid_scale = null; | ||
787 | |||
788 | this.gdes = []; | ||
789 | |||
790 | this.ytr_pixie = 0; | ||
791 | this.xtr_pixie = 0; | ||
792 | |||
793 | this.AlmostEqualBuffer = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT*2); | ||
794 | this.AlmostEqualInt = new Int32Array(this.AlmostEqualBuffer); | ||
795 | this.AlmostEqualFloat = new Float32Array(this.AlmostEqualBuffer); | ||
796 | |||
797 | this.DEFAULT_FONT = 'DejaVu Sans Mono'; //DejaVu Sans Mono ,Bitstream Vera Sans Mono,monospace,Courier', // pt -> pt=px*72/96 | ||
798 | this.MGRIDWIDTH = 0.6; | ||
799 | this.GRIDWIDTH = 0.4; | ||
800 | this.YLEGEND_ANGLE = 90.0; | ||
801 | |||
802 | this.TEXT = { | ||
803 | DEFAULT: { size: 11, font: this.DEFAULT_FONT }, | ||
804 | TITLE: { size: 12, font: this.DEFAULT_FONT }, | ||
805 | AXIS: { size: 10, font: this.DEFAULT_FONT }, | ||
806 | UNIT: { size: 11, font: this.DEFAULT_FONT }, | ||
807 | LEGEND: { size: 11, font: this.DEFAULT_FONT }, | ||
808 | WATERMARK: { size: 8, font: this.DEFAULT_FONT } | ||
809 | }; | ||
810 | |||
811 | this.GRC = { | ||
812 | CANVAS: 'rgba(255, 255, 255, 1.0)', | ||
813 | BACK: 'rgba(242,242, 242, 1.0)', | ||
814 | SHADEA: 'rgba(207, 207, 207, 1.0)', | ||
815 | SHADEB: 'rgba(158, 158, 158, 1.0)', | ||
816 | GRID: 'rgba(143, 143, 143, 0.75)', | ||
817 | MGRID: 'rgba(222, 79, 79, 0.60)', | ||
818 | FONT: 'rgba(0, 0, 0, 1.0)', | ||
819 | ARROW: 'rgba(127, 31, 31, 1.0)', | ||
820 | AXIS: 'rgba(31, 31, 31, 1.0)', | ||
821 | FRAME: 'rgba(0, 0, 0, 1.0)' | ||
822 | }; | ||
823 | |||
824 | this.xlab = [ | ||
825 | {minsec: 0, length: 0, gridtm: RrdGraph.TMT_SECOND, gridst: 30, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 5, labtm: RrdGraph.TMT_MINUTE, labst: 5, precis: 0, stst: '%H:%M' } , | ||
826 | {minsec: 2, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 1, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 5, labtm: RrdGraph.TMT_MINUTE, labst: 5, precis: 0, stst: '%H:%M' } , | ||
827 | {minsec: 5, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 2, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 10, labtm: RrdGraph.TMT_MINUTE, labst: 10, precis: 0, stst: '%H:%M' } , | ||
828 | {minsec: 10, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 5,mgridtm: RrdGraph.TMT_MINUTE, mgridst: 20, labtm: RrdGraph.TMT_MINUTE, labst: 20, precis: 0, stst: '%H:%M' } , | ||
829 | {minsec: 30, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 10, mgridtm: RrdGraph.TMT_HOUR, mgridst: 1, labtm: RrdGraph.TMT_HOUR, labst: 1, precis: 0, stst: '%H:%M' } , | ||
830 | {minsec: 60, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 30, mgridtm: RrdGraph.TMT_HOUR, mgridst: 2, labtm: RrdGraph.TMT_HOUR, labst: 2, precis: 0, stst: '%H:%M' } , | ||
831 | {minsec: 60, length: 24 * 3600, gridtm: RrdGraph.TMT_MINUTE, gridst: 30, mgridtm: RrdGraph.TMT_HOUR, mgridst: 2, labtm: RrdGraph.TMT_HOUR, labst: 6, precis: 0, stst: '%a %H:%M' } , | ||
832 | {minsec: 180, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 1, mgridtm: RrdGraph.TMT_HOUR, mgridst: 6, labtm: RrdGraph.TMT_HOUR, labst: 6, precis: 0, stst: '%H:%M' } , | ||
833 | {minsec: 180, length: 24 * 3600, gridtm: RrdGraph.TMT_HOUR, gridst: 1, mgridtm: RrdGraph.TMT_HOUR, mgridst: 6, labtm: RrdGraph.TMT_HOUR, labst: 12, precis: 0, stst: '%a %H:%M' } , | ||
834 | {minsec: 600, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 6, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 1, precis: 24 * 3600, stst: '%a' } , | ||
835 | {minsec: 1200, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 6, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 1, precis: 24 * 3600, stst: '%d' } , | ||
836 | {minsec: 1800, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 12, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 2, precis: 24 * 3600, stst: '%a %d' }, | ||
837 | {minsec: 2400, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 12, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 2, precis: 24 * 3600,stst: '%a' }, | ||
838 | {minsec: 3600, length: 0, gridtm: RrdGraph.TMT_DAY, gridst: 1, mgridtm: RrdGraph.TMT_WEEK, mgridst: 1, labtm: RrdGraph.TMT_WEEK, labst: 1, precis: 7 * 24 * 3600, stst: 'Week %V' }, | ||
839 | {minsec: 3 * 3600, length: 0, gridtm: RrdGraph.TMT_WEEK, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 1, labtm: RrdGraph.TMT_WEEK, labst: 2, precis: 7 * 24 * 3600, stst: 'Week %V' }, | ||
840 | {minsec: 6 * 3600, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 1, labtm: RrdGraph.TMT_MONTH, labst: 1, precis: 30 * 24 * 3600, stst: '%b' }, | ||
841 | {minsec: 48 * 3600,length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 3, labtm: RrdGraph.TMT_MONTH, labst: 3, precis: 30 * 24 * 3600, stst: '%b' }, | ||
842 | {minsec: 315360, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 3, mgridtm: RrdGraph.TMT_YEAR, mgridst: 1, labtm: RrdGraph.TMT_YEAR, labst: 1, precis: 365 * 24 * 3600, stst: '%Y' }, | ||
843 | {minsec: 10 * 24 * 3600 , length: 0, gridtm: RrdGraph.TMT_YEAR, gridst: 1, mgridtm: RrdGraph.TMT_YEAR, mgridst: 1, labtm: RrdGraph.TMT_YEAR, labst: 1, precis: 365 * 24 * 3600, stst: '%y' }, | ||
844 | {minsec: -1, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 0, mgridtm: RrdGraph.TMT_MONTH, mgridst: 0, labtm: RrdGraph.TMT_MONTH, labst: 0, precis: 0, stst: null } | ||
845 | ]; | ||
846 | |||
847 | this.ylab = [ | ||
848 | {grid: 0.1, lfac: [1, 2, 5, 10] } , | ||
849 | {grid: 0.2, lfac: [1, 5, 10, 20] } , | ||
850 | {grid: 0.5, lfac: [1, 2, 4, 10] } , | ||
851 | {grid: 1.0, lfac: [1, 2, 5, 10] } , | ||
852 | {grid: 2.0, lfac: [1, 5, 10, 20] } , | ||
853 | {grid: 5.0, lfac: [1, 2, 4, 10] } , | ||
854 | {grid: 10.0, lfac: [1, 2, 5, 10] } , | ||
855 | {grid: 20.0, lfac: [1, 5, 10, 20] } , | ||
856 | {grid: 50.0, lfac: [1, 2, 4, 10] } , | ||
857 | {grid: 100.0, lfac: [1, 2, 5, 10] } , | ||
858 | {grid: 200.0, lfac: [1, 5, 10, 20] } , | ||
859 | {grid: 500.0, lfac: [1, 2, 4, 10] }, | ||
860 | {grid: 0.0, lfac: [0, 0, 0, 0] } | ||
861 | ]; | ||
862 | |||
863 | this.si_symbol = [ | ||
864 | 'y', /* 10e-24 Yocto */ | ||
865 | 'z', /* 10e-21 Zepto */ | ||
866 | 'a', /* 10e-18 Atto */ | ||
867 | 'f', /* 10e-15 Femto */ | ||
868 | 'p', /* 10e-12 Pico */ | ||
869 | 'n', /* 10e-9 Nano */ | ||
870 | 'u', /* 10e-6 Micro */ | ||
871 | 'm', /* 10e-3 Milli */ | ||
872 | ' ', /* Base */ | ||
873 | 'k', /* 10e3 Kilo */ | ||
874 | 'M', /* 10e6 Mega */ | ||
875 | 'G', /* 10e9 Giga */ | ||
876 | 'T', /* 10e12 Tera */ | ||
877 | 'P', /* 10e15 Peta */ | ||
878 | 'E', /* 10e18 Exa */ | ||
879 | 'Z', /* 10e21 Zeta */ | ||
880 | 'Y' /* 10e24 Yotta */ | ||
881 | ]; | ||
882 | this.si_symbcenter = 8; | ||
883 | |||
884 | this.start_t = new RrdTime("end-24h"); | ||
885 | this.end_t = new RrdTime("now"); | ||
886 | }; | ||
887 | |||
888 | RrdGraph.TMT_SECOND = 0; | ||
889 | RrdGraph.TMT_MINUTE = 1; | ||
890 | RrdGraph.TMT_HOUR = 2; | ||
891 | RrdGraph.TMT_DAY = 3; | ||
892 | RrdGraph.TMT_WEEK = 4; | ||
893 | RrdGraph.TMT_MONTH = 5; | ||
894 | RrdGraph.TMT_YEAR = 6; | ||
895 | |||
896 | RrdGraph.GFX_H_LEFT = 1; | ||
897 | RrdGraph.GFX_H_RIGHT = 2; | ||
898 | RrdGraph.GFX_H_CENTER = 3; | ||
899 | |||
900 | RrdGraph.GFX_V_TOP = 1; | ||
901 | RrdGraph.GFX_V_BOTTOM = 2; | ||
902 | RrdGraph.GFX_V_CENTER = 3; | ||
903 | |||
904 | RrdGraph.LEGEND_POS_NORTH = 0; | ||
905 | RrdGraph.LEGEND_POS_WEST = 1; | ||
906 | RrdGraph.LEGEND_POS_SOUTH = 2; | ||
907 | RrdGraph.LEGEND_POS_EAST = 3; | ||
908 | |||
909 | RrdGraph.LEGEND_DIR_TOP_DOWN = 0; | ||
910 | RrdGraph.LEGEND_DIR_BOTTOM_UP = 1; | ||
911 | |||
912 | RrdGraph.prototype.set_default_font = function (name) | ||
913 | { | ||
914 | for (var font in this.TEXT) | ||
915 | this.TEXT[font].font = name; | ||
916 | }; | ||
917 | |||
918 | RrdGraph.prototype.parse_color = function(str) | ||
919 | { | ||
920 | var bits; | ||
921 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) { | ||
922 | return [parseInt(bits[1]+bits[1], 16), parseInt(bits[2]+bits[2], 16), parseInt(bits[3]+bits[3], 16), 1.0]; | ||
923 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
924 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), 1.0]; | ||
925 | } 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))) { | ||
926 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), parseInt(bits[4], 16)/255]; | ||
927 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) { | ||
928 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), 1.0]; | ||
929 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) { | ||
930 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), parseFloat(bits[4], 10)]; | ||
931 | } else { | ||
932 | throw new RrdGraphError("Unknow color format '"+str+"'"); | ||
933 | } | ||
934 | }; | ||
935 | |||
936 | RrdGraph.prototype.color2rgba = function (color) | ||
937 | { | ||
938 | return 'rgba('+color[0]+','+color[1]+','+color[2]+','+color[3]+')'; | ||
939 | }; | ||
940 | |||
941 | RrdGraph.prototype.xtr = function (mytime) | ||
942 | { | ||
943 | if (mytime === 0) { | ||
944 | this.xtr_pixie = this.xsize / (this.end - this.start); | ||
945 | return this.xorigin; | ||
946 | } | ||
947 | return this.xorigin + this.xtr_pixie * (mytime - this.start); | ||
948 | }; | ||
949 | |||
950 | RrdGraph.prototype.ytr = function (value) | ||
951 | { | ||
952 | var yval; | ||
953 | |||
954 | if (isNaN(value)) { | ||
955 | if (!this.logarithmic) | ||
956 | this.ytr_pixie = this.ysize / (this.maxval - this.minval); | ||
957 | else | ||
958 | this.ytr_pixie = this.ysize / ((Math.log(this.maxval)/Math.LN10) - (Math.log(this.minval)/Math.LN10)); | ||
959 | yval = this.yorigin; | ||
960 | } else if (!this.logarithmic) { | ||
961 | yval = this.yorigin - this.ytr_pixie * (value - this.minval); | ||
962 | } else { | ||
963 | if (value < this.minval) { | ||
964 | yval = this.yorigin; | ||
965 | } else { | ||
966 | yval = this.yorigin - this.ytr_pixie * ((Math.log(value)/Math.LN10) - (Math.log(this.minval)/Math.LN10)); | ||
967 | } | ||
968 | } | ||
969 | return yval; | ||
970 | }; | ||
971 | |||
972 | RrdGraph.prototype.AlmostEqual2sComplement = function(A, B, maxUlps) | ||
973 | { | ||
974 | this.AlmostEqualFloat[0] = A; | ||
975 | this.AlmostEqualFloat[1] = B; | ||
976 | |||
977 | var aInt = this.AlmostEqualInt[0]; | ||
978 | if (aInt < 0) aInt = 0x80000000 - aInt; | ||
979 | |||
980 | var bInt = this.AlmostEqualInt[1]; | ||
981 | if (bInt < 0) bInt = 0x80000000 - bInt; | ||
982 | |||
983 | var intDiff = Math.abs(aInt - bInt); | ||
984 | |||
985 | if (intDiff <= maxUlps) | ||
986 | return true; | ||
987 | |||
988 | return false; | ||
989 | }; | ||
990 | |||
991 | RrdGraph.prototype.tmt2str = function (val) | ||
992 | { | ||
993 | switch (val) { | ||
994 | case RrdGraph.TMT_SECOND: return 'sec'; | ||
995 | case RrdGraph.TMT_MINUTE: return 'min'; | ||
996 | case RrdGraph.TMT_HOUR: return 'hour'; | ||
997 | case RrdGraph.TMT_DAY: return 'day'; | ||
998 | case RrdGraph.TMT_WEEK: return 'week'; | ||
999 | case RrdGraph.TMT_MONTH: return 'mon'; | ||
1000 | case RrdGraph.TMT_YEAR: return 'year'; | ||
1001 | } | ||
1002 | return val; | ||
1003 | }; | ||
1004 | |||
1005 | RrdGraph.prototype.find_first_time = function(start, baseint, basestep) | ||
1006 | { | ||
1007 | var date = new Date(start*1000); | ||
1008 | |||
1009 | switch (baseint) { | ||
1010 | case RrdGraph.TMT_SECOND: | ||
1011 | var sec = date.getSeconds(); | ||
1012 | sec -= sec % basestep; | ||
1013 | date.setSeconds(sec); | ||
1014 | break; | ||
1015 | case RrdGraph.TMT_MINUTE: | ||
1016 | date.setSeconds(0); | ||
1017 | var min = date.getMinutes(); | ||
1018 | min -= min % basestep; | ||
1019 | date.setMinutes(min); | ||
1020 | break; | ||
1021 | case RrdGraph.TMT_HOUR: | ||
1022 | date.setSeconds(0); | ||
1023 | date.setMinutes(0); | ||
1024 | var hour = date.getHours(); | ||
1025 | hour -= hour % basestep; | ||
1026 | date.setHours(hour); | ||
1027 | break; | ||
1028 | case RrdGraph.TMT_DAY: | ||
1029 | date.setSeconds(0); | ||
1030 | date.setMinutes(0); | ||
1031 | date.setHours(0); | ||
1032 | break; | ||
1033 | case RrdGraph.TMT_WEEK: | ||
1034 | date.setSeconds(0); | ||
1035 | date.setMinutes(0); | ||
1036 | date.setHours(0); | ||
1037 | var mday = date.getDate(); | ||
1038 | var wday = date.getDay(); | ||
1039 | mday -= wday - 1; // FIXME find_first_weekday | ||
1040 | if (wday === 0) mday -= 7;// FIXME find_first_weekday | ||
1041 | date.setDate(mday); | ||
1042 | break; | ||
1043 | case RrdGraph.TMT_MONTH: | ||
1044 | date.setSeconds(0); | ||
1045 | date.setMinutes(0); | ||
1046 | date.setHours(0); | ||
1047 | date.setDate(1); | ||
1048 | var mon = date.getMonth(); | ||
1049 | mon -= mon % basestep; | ||
1050 | date.setMonth(mon); | ||
1051 | break; | ||
1052 | case RrdGraph.TMT_YEAR: | ||
1053 | date.setSeconds(0); | ||
1054 | date.setMinutes(0); | ||
1055 | date.setHours(0); | ||
1056 | date.setDate(1); | ||
1057 | date.setMonth(0); | ||
1058 | var year = date.getFullYear()-1900; | ||
1059 | year -= (year + 1900) %basestep; | ||
1060 | date.setFullYear(year+1900); | ||
1061 | } | ||
1062 | return Math.round(date.getTime()/1000.0); | ||
1063 | }; | ||
1064 | |||
1065 | RrdGraph.prototype.find_next_time = function(current, baseint, basestep) | ||
1066 | { | ||
1067 | var date = new Date(current*1000); | ||
1068 | var limit = 2; | ||
1069 | var madetime; | ||
1070 | |||
1071 | switch (baseint) { | ||
1072 | case RrdGraph.TMT_SECOND: limit = 7200; break; | ||
1073 | case RrdGraph.TMT_MINUTE: limit = 120; break; | ||
1074 | case RrdGraph.TMT_HOUR: limit = 2; break; | ||
1075 | default: limit = 2; break; | ||
1076 | } | ||
1077 | |||
1078 | do { | ||
1079 | switch (baseint) { | ||
1080 | case RrdGraph.TMT_SECOND: | ||
1081 | date.setSeconds(date.getSeconds()+basestep); | ||
1082 | break; | ||
1083 | case RrdGraph.TMT_MINUTE: | ||
1084 | date.setMinutes(date.getMinutes()+basestep); | ||
1085 | break; | ||
1086 | case RrdGraph.TMT_HOUR: | ||
1087 | date.setHours(date.getHours()+basestep); | ||
1088 | break; | ||
1089 | case RrdGraph.TMT_DAY: | ||
1090 | date.setDate(date.getDate()+basestep); | ||
1091 | break; | ||
1092 | case RrdGraph.TMT_WEEK: | ||
1093 | date.setDate(date.getDate()+7*basestep); | ||
1094 | break; | ||
1095 | case RrdGraph.TMT_MONTH: | ||
1096 | date.setMonth(date.getMonth()+basestep); | ||
1097 | break; | ||
1098 | case RrdGraph.TMT_YEAR: | ||
1099 | date.setFullYear(date.getFullYear()+basestep); | ||
1100 | break; | ||
1101 | } | ||
1102 | madetime = Math.round(date.getTime()/1000.0); | ||
1103 | } while (madetime === -1 && limit-- >= 0); /* this is necessary to skip impossible times like the daylight saving time skips */ // FIXME ?? | ||
1104 | return madetime; | ||
1105 | }; | ||
1106 | |||
1107 | RrdGraph.prototype.print_calc = function() | ||
1108 | { | ||
1109 | var validsteps; | ||
1110 | var printval; | ||
1111 | var tmvdef; | ||
1112 | var graphelement = 0; | ||
1113 | var max_ii; | ||
1114 | var magfact = -1; | ||
1115 | var si_symb = ""; | ||
1116 | var percent_s; | ||
1117 | var prline_cnt = 0; | ||
1118 | |||
1119 | var now = Math.round((new Date()).getTime() / 1000); | ||
1120 | |||
1121 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
1122 | var vidx = this.gdes[i].vidx; | ||
1123 | switch (this.gdes[i].gf) { | ||
1124 | case RrdGraphDesc.GF_PRINT: | ||
1125 | case RrdGraphDesc.GF_GPRINT: | ||
1126 | if (this.gdes[vidx].gf === RrdGraphDesc.GF_VDEF) { /* simply use vals */ | ||
1127 | printval = this.gdes[vidx].vf.val; | ||
1128 | tmvdef = this.gdes[vidx].vf.when; | ||
1129 | // localtime_r(&this.gdes[vidx].vf.when, &tmvdef); // FIXME ? | ||
1130 | } else { /* need to calculate max,min,avg etcetera */ | ||
1131 | max_ii = ((this.gdes[vidx].end - this.gdes[vidx].start) / this.gdes[vidx].step * this.gdes[vidx].ds_cnt); | ||
1132 | printval = Number.NaN; | ||
1133 | validsteps = 0; | ||
1134 | |||
1135 | for (var ii = this.gdes[vidx].ds; ii < max_ii; ii += this.gdes[vidx].ds_cnt) { | ||
1136 | if (!isFinite(this.gdes[vidx].data[ii])) continue; | ||
1137 | if (isNaN(printval)) { | ||
1138 | printval = this.gdes[vidx].data[ii]; | ||
1139 | validsteps++; | ||
1140 | continue; | ||
1141 | } | ||
1142 | switch (this.gdes[i].cf) { | ||
1143 | case RrdGraphDesc.CF_HWPREDICT: | ||
1144 | case RrdGraphDesc.CF_MHWPREDICT: | ||
1145 | case RrdGraphDesc.CF_DEVPREDICT: | ||
1146 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
1147 | case RrdGraphDesc.CF_SEASONAL: | ||
1148 | case RrdGraphDesc.CF_AVERAGE: | ||
1149 | validsteps++; | ||
1150 | printval += this.gdes[vidx].data[ii]; | ||
1151 | break; | ||
1152 | case RrdGraphDesc.CF_MINIMUM: | ||
1153 | printval = Math.min(printval, this.gdes[vidx].data[ii]); | ||
1154 | break; | ||
1155 | case RrdGraphDesc.CF_FAILURES: | ||
1156 | case RrdGraphDesc.CF_MAXIMUM: | ||
1157 | printval = Math.max(printval, this.gdes[vidx].data[ii]); | ||
1158 | break; | ||
1159 | case RrdGraphDesc.CF_LAST: | ||
1160 | printval = this.gdes[vidx].data[ii]; | ||
1161 | } | ||
1162 | } | ||
1163 | if (this.gdes[i].cf === RrdGraphDesc.CF_AVERAGE || this.gdes[i].cf > RrdGraphDesc.CF_LAST) { | ||
1164 | if (validsteps > 1) printval = (printval / validsteps); | ||
1165 | } | ||
1166 | } /* prepare printval */ | ||
1167 | |||
1168 | if (!this.gdes[i].strftm && (percent_s = this.gdes[i].format.indexOf('%S')) != -1) { | ||
1169 | if (magfact < 0.0) { | ||
1170 | //[printval, si_symb, magfact] = this.auto_scale(printval, si_symb, magfact); | ||
1171 | var dummy = this.auto_scale(printval, si_symb, magfact); printval = dummy[0]; si_symb = dummy[1]; magfact = dummy[2]; | ||
1172 | if (printval === 0.0) magfact = -1.0; | ||
1173 | } else { | ||
1174 | printval /= magfact; | ||
1175 | } | ||
1176 | this.gdes[i].format = this.gdes[i].format.substr(0, percent_s+1)+'s'+this.gdes[i].format.substr(percent_s+2); | ||
1177 | } else if (!this.gdes[i].strftm && this.gdes[i].format.indexOf('%s') != -1) { | ||
1178 | //[printval, si_symb, magfact] = this.auto_scale(printval, si_symb, magfact); | ||
1179 | var dummy = this.auto_scale(printval, si_symb, magfact); printval = dummy[0]; si_symb = dummy[1]; magfact = dummy[2]; | ||
1180 | } | ||
1181 | |||
1182 | if (this.gdes[i].strftm) { | ||
1183 | this.gdes[i].legend = strftime(this.gdes[i].format, tmvdef); | ||
1184 | } else { | ||
1185 | this.gdes[i].legend = sprintf(this.gdes[i].format, printval, si_symb); | ||
1186 | } | ||
1187 | graphelement = 1; | ||
1188 | break; | ||
1189 | case RrdGraphDesc.GF_LINE: | ||
1190 | case RrdGraphDesc.GF_AREA: | ||
1191 | case RrdGraphDesc.GF_TICK: | ||
1192 | graphelement = 1; | ||
1193 | break; | ||
1194 | case RrdGraphDesc.GF_HRULE: | ||
1195 | if (isNaN(this.gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */ | ||
1196 | this.gdes[i].yrule = this.gdes[vidx].vf.val; | ||
1197 | }; | ||
1198 | graphelement = 1; | ||
1199 | break; | ||
1200 | case RrdGraphDesc.GF_VRULE: | ||
1201 | if (this.gdes[i].xrule === 0) { /* again ... the legend printer needs it */ | ||
1202 | this.gdes[i].xrule = this.gdes[vidx].vf.when; | ||
1203 | }; | ||
1204 | graphelement = 1; | ||
1205 | break; | ||
1206 | case RrdGraphDesc.GF_COMMENT: | ||
1207 | case RrdGraphDesc.GF_TEXTALIGN: | ||
1208 | case RrdGraphDesc.GF_DEF: | ||
1209 | case RrdGraphDesc.GF_CDEF: | ||
1210 | case RrdGraphDesc.GF_VDEF: | ||
1211 | case RrdGraphDesc.GF_SHIFT: | ||
1212 | case RrdGraphDesc.GF_XPORT: | ||
1213 | break; | ||
1214 | case RrdGraphDesc.GF_STACK: | ||
1215 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
1216 | break; | ||
1217 | } | ||
1218 | } | ||
1219 | return graphelement; | ||
1220 | }; | ||
1221 | |||
1222 | RrdGraph.prototype.reduce_data = function(gdes, cur_step) | ||
1223 | { | ||
1224 | var reduce_factor = Math.ceil(gdes.step / cur_step); | ||
1225 | var col, dst_row, row_cnt, start_offset, end_offset, skiprows = 0; | ||
1226 | var srcptr, dstptr; | ||
1227 | |||
1228 | gdes.step = cur_step * reduce_factor; /* set new step size for reduced data */ | ||
1229 | dstptr = 0; | ||
1230 | srcptr = 0; | ||
1231 | row_cnt = (gdes.end - gdes.start) / cur_step; | ||
1232 | |||
1233 | end_offset = gdes.end % gdes.step; | ||
1234 | start_offset = gdes.start % gdes.step; | ||
1235 | |||
1236 | if (start_offset) { | ||
1237 | gdes.start = gdes.start - start_offset; | ||
1238 | skiprows = reduce_factor - start_offset / cur_step; | ||
1239 | srcptr += skiprows * gdes.ds_cnt; | ||
1240 | for (col = 0; col < gdes.ds_cnt; col++) | ||
1241 | gdes.data[dstptr++] = Number.NaN; | ||
1242 | row_cnt -= skiprows; | ||
1243 | } | ||
1244 | |||
1245 | if (end_offset) { | ||
1246 | gdes.end = gdes.end - end_offset + gdes.step; | ||
1247 | skiprows = end_offset / cur_step; | ||
1248 | row_cnt -= skiprows; | ||
1249 | } | ||
1250 | |||
1251 | if (row_cnt % reduce_factor) { | ||
1252 | throw new RrdGraphError("BUG in reduce_data(), SANITY CHECK: "+row_cnt+" rows cannot be reduced by "+reduce_factor); | ||
1253 | } | ||
1254 | |||
1255 | for (dst_row = 0; row_cnt >= reduce_factor; dst_row++) { | ||
1256 | for (col = 0; col < gdes.ds_cnt; col++) { | ||
1257 | var newval = Number.NaN; | ||
1258 | var validval = 0; | ||
1259 | |||
1260 | for (var i = 0; i < reduce_factor; i++) { | ||
1261 | if (isNaN(gdes.data[srcptr + i*gdes.ds_cnt + col])) continue; | ||
1262 | validval++; | ||
1263 | if (isNaN(newval)) { | ||
1264 | newval = gdes.data[srcptr + i * gdes.ds_cnt + col]; | ||
1265 | } else { | ||
1266 | switch (gdes.cf_reduce) { | ||
1267 | case RrdGraphDesc.CF_HWPREDICT: | ||
1268 | case RrdGraphDesc.CF_MHWPREDICT: | ||
1269 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
1270 | case RrdGraphDesc.CF_DEVPREDICT: | ||
1271 | case RrdGraphDesc.CF_SEASONAL: | ||
1272 | case RrdGraphDesc.CF_AVERAGE: | ||
1273 | newval += gdes.data[srcptr + i*gdes.ds_cnt + col]; | ||
1274 | break; | ||
1275 | case RrdGraphDesc.CF_MINIMUM: | ||
1276 | newval = Math.min(newval, gdes.data[srcptr + i*gdes.ds_cnt + col]); | ||
1277 | break; | ||
1278 | case RrdGraphDesc.CF_FAILURES: | ||
1279 | /* an interval contains a failure if any subintervals contained a failure */ | ||
1280 | case RrdGraphDesc.CF_MAXIMUM: | ||
1281 | newval = Math.max(newval, gdes.data[srcptr + i*gdes.ds_cnt + col]); | ||
1282 | break; | ||
1283 | case RrdGraphDesc.CF_LAST: | ||
1284 | newval = gdes.data[srcptr + i*gdes.ds_cnt + col]; | ||
1285 | break; | ||
1286 | } | ||
1287 | } | ||
1288 | } | ||
1289 | |||
1290 | if (validval === 0) { | ||
1291 | newval = Number.NaN; | ||
1292 | } else { | ||
1293 | switch (gdes.cf_reduce) { | ||
1294 | case RrdGraphDesc.CF_HWPREDICT: | ||
1295 | case RrdGraphDesc.CF_MHWPREDICT: | ||
1296 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
1297 | case RrdGraphDesc.CF_DEVPREDICT: | ||
1298 | case RrdGraphDesc.CF_SEASONAL: | ||
1299 | case RrdGraphDesc.CF_AVERAGE: | ||
1300 | newval /= validval; | ||
1301 | break; | ||
1302 | case RrdGraphDesc.CF_MINIMUM: | ||
1303 | case RrdGraphDesc.CF_FAILURES: | ||
1304 | case RrdGraphDesc.CF_MAXIMUM: | ||
1305 | case RrdGraphDesc.CF_LAST: | ||
1306 | break; | ||
1307 | } | ||
1308 | } | ||
1309 | gdes.data[dstptr++] = newval; | ||
1310 | } | ||
1311 | |||
1312 | srcptr += gdes.ds_cnt * reduce_factor; | ||
1313 | row_cnt -= reduce_factor; | ||
1314 | } | ||
1315 | if (end_offset) { | ||
1316 | for (col = 0; col < gdes.ds_cnt; col++) | ||
1317 | gdes.data[dstptr++] = Number.NaN; | ||
1318 | } | ||
1319 | }; | ||
1320 | |||
1321 | RrdGraph.prototype.data_fetch = function() | ||
1322 | { | ||
1323 | var skip; | ||
1324 | |||
1325 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
1326 | if (this.gdes[i].gf != RrdGraphDesc.GF_DEF) continue; | ||
1327 | |||
1328 | skip = false; | ||
1329 | for (var ii = 0; ii < i; ii++) { | ||
1330 | if (this.gdes[ii].gf != RrdGraphDesc.GF_DEF) continue; | ||
1331 | if ((this.gdes[i].rrd === this.gdes[ii].rrd) | ||
1332 | && (this.gdes[i].cf === this.gdes[ii].cf) | ||
1333 | && (this.gdes[i].cf_reduce === this.gdes[ii].cf_reduce) | ||
1334 | && (this.gdes[i].start_orig === this.gdes[ii].start_orig) | ||
1335 | && (this.gdes[i].end_orig === this.gdes[ii].end_orig) | ||
1336 | && (this.gdes[i].step_orig === this.gdes[ii].step_orig)) { | ||
1337 | this.gdes[i].start = this.gdes[ii].start; | ||
1338 | this.gdes[i].end = this.gdes[ii].end; | ||
1339 | this.gdes[i].step = this.gdes[ii].step; | ||
1340 | this.gdes[i].ds_cnt = this.gdes[ii].ds_cnt; | ||
1341 | this.gdes[i].ds_namv = this.gdes[ii].ds_namv; | ||
1342 | this.gdes[i].data = this.gdes[ii].data; | ||
1343 | this.gdes[i].data_first = 0; | ||
1344 | skip = true; | ||
1345 | } | ||
1346 | if (skip) break; | ||
1347 | } | ||
1348 | |||
1349 | if (!skip) { | ||
1350 | var ft_step = this.gdes[i].step; /* ft_step will record what we got from fetch */ | ||
1351 | ft_step = this.data.fetch(this.gdes[i], ft_step); | ||
1352 | if (ft_step < 0) | ||
1353 | return -1; | ||
1354 | this.gdes[i].data_first = 1; | ||
1355 | // this.gdes[i].step = Math.max(this.gdes[i].step, this.step); // FIXME | ||
1356 | if (ft_step < this.gdes[i].step) { | ||
1357 | this.reduce_data(this.gdes[i], ft_step); | ||
1358 | } else { | ||
1359 | this.gdes[i].step = ft_step; | ||
1360 | } | ||
1361 | } | ||
1362 | /* lets see if the required data source is really there */ | ||
1363 | for (var ii = 0; ii < this.gdes[i].ds_cnt; ii++) { | ||
1364 | if (this.gdes[i].ds_namv[ii] === this.gdes[i].ds_nam) { | ||
1365 | this.gdes[i].ds = ii; | ||
1366 | break; | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | if (this.gdes[i].ds === -1) | ||
1371 | throw new RrdGraphError("No DS called '"+this.gdes[i].ds_nam+"' in '"+this.gdes[i].rrd+"'"); | ||
1372 | } | ||
1373 | |||
1374 | return 0; | ||
1375 | }; | ||
1376 | |||
1377 | RrdGraph.prototype.lcd = function (num) | ||
1378 | { | ||
1379 | var rest; | ||
1380 | for (var i = 0; num[i + 1] != 0; i++) { | ||
1381 | do { | ||
1382 | rest = num[i] % num[i + 1]; | ||
1383 | num[i] = num[i + 1]; | ||
1384 | num[i + 1] = rest; | ||
1385 | } while (rest != 0); // FIXME infinite loop ? | ||
1386 | num[i + 1] = num[i]; | ||
1387 | } | ||
1388 | return num[i]; | ||
1389 | }; | ||
1390 | |||
1391 | RrdGraph.prototype.data_calc = function() | ||
1392 | { | ||
1393 | var dataidx; | ||
1394 | var now; | ||
1395 | var rpnstack; | ||
1396 | |||
1397 | for (var gdi = 0, gdes_c = this.gdes.length; gdi < gdes_c; gdi++) { | ||
1398 | switch (this.gdes[gdi].gf) { | ||
1399 | case RrdGraphDesc.GF_XPORT: | ||
1400 | break; | ||
1401 | case RrdGraphDesc.GF_SHIFT: | ||
1402 | var vdp = this.gdes[this.gdes[gdi].vidx]; | ||
1403 | /* remove current shift */ | ||
1404 | vdp.start -= vdp.shift; | ||
1405 | vdp.end -= vdp.shift; | ||
1406 | |||
1407 | if (this.gdes[gdi].shidx >= 0) vdp.shift = this.gdes[this.gdes[gdi].shidx].vf.val; | ||
1408 | else vdp.shift = this.gdes[gdi].shval; | ||
1409 | |||
1410 | vdp.shift = (vdp.shift / vdp.step) * vdp.step; | ||
1411 | |||
1412 | vdp.start += vdp.shift; | ||
1413 | vdp.end += vdp.shift; | ||
1414 | break; | ||
1415 | case RrdGraphDesc.GF_VDEF: | ||
1416 | this.gdes[gdi].ds_cnt = 0; | ||
1417 | if (this.gdes[gdi].vf.calc(this.gdes[this.gdes[gdi].vidx])) | ||
1418 | throw new RrdGraphError("Error processing VDEF '"+this.gdes[gdi].vname+"'"); | ||
1419 | break; | ||
1420 | case RrdGraphDesc.GF_CDEF: | ||
1421 | this.gdes[gdi].ds_cnt = 1; | ||
1422 | this.gdes[gdi].ds = 0; | ||
1423 | this.gdes[gdi].data_first = 1; | ||
1424 | this.gdes[gdi].start = 0; | ||
1425 | this.gdes[gdi].end = 0; | ||
1426 | var steparray = []; | ||
1427 | var stepcnt = 0; | ||
1428 | dataidx = -1; | ||
1429 | |||
1430 | var rpnp = this.gdes[gdi].rpnp.rpnp; | ||
1431 | for (var rpi = 0; rpnp[rpi].op != RrdRpn.OP_END; rpi++) { | ||
1432 | if (rpnp[rpi].op === RrdRpn.OP_VARIABLE || rpnp[rpi].op === RrdRpn.OP_PREV_OTHER) { | ||
1433 | var ptr = rpnp[rpi].ptr; | ||
1434 | if (this.gdes[ptr].ds_cnt === 0) { /* this is a VDEF data source */ | ||
1435 | rpnp[rpi].val = this.gdes[ptr].vf.val; | ||
1436 | rpnp[rpi].op = RrdRpn.OP_NUMBER; | ||
1437 | } else { /* normal variables and PREF(variables) */ | ||
1438 | ++stepcnt; | ||
1439 | steparray[stepcnt - 1] = this.gdes[ptr].step; | ||
1440 | |||
1441 | if (this.gdes[gdi].start < this.gdes[ptr].start) | ||
1442 | this.gdes[gdi].start = this.gdes[ptr].start; | ||
1443 | if (this.gdes[gdi].end === 0 || this.gdes[gdi].end > this.gdes[ptr].end) | ||
1444 | this.gdes[gdi].end = this.gdes[ptr].end; | ||
1445 | |||
1446 | rpnp[rpi].data = this.gdes[ptr].data; | ||
1447 | rpnp[rpi].pdata = this.gdes[ptr].ds; | ||
1448 | rpnp[rpi].step = this.gdes[ptr].step; | ||
1449 | rpnp[rpi].ds_cnt = this.gdes[ptr].ds_cnt; | ||
1450 | } | ||
1451 | } | ||
1452 | } | ||
1453 | /* move the data pointers to the correct period */ | ||
1454 | for (var rpi = 0; rpnp[rpi].op != RrdRpn.OP_END; rpi++) { | ||
1455 | if (rpnp[rpi].op === RrdRpn.OP_VARIABLE || rpnp[rpi].op === RrdRpn.OP_PREV_OTHER) { | ||
1456 | var ptr = rpnp[rpi].ptr; | ||
1457 | var diff = this.gdes[gdi].start - this.gdes[ptr].start; | ||
1458 | |||
1459 | if (diff > 0) rpnp[rpi].pdata += (diff / this.gdes[ptr].step) * this.gdes[ptr].ds_cnt; | ||
1460 | } | ||
1461 | } | ||
1462 | |||
1463 | if (steparray === null) { | ||
1464 | throw new RrdGraphError("rpn expressions without DEF or CDEF variables are not supported"); | ||
1465 | } | ||
1466 | steparray[stepcnt] = 0; | ||
1467 | this.gdes[gdi].step = this.lcd(steparray); | ||
1468 | this.gdes[gdi].data = []; | ||
1469 | |||
1470 | for (now = this.gdes[gdi].start + this.gdes[gdi].step; now <= this.gdes[gdi].end; now += this.gdes[gdi].step) { | ||
1471 | if (this.gdes[gdi].rpnp.calc(now, this.gdes[gdi].data, ++dataidx) === -1) | ||
1472 | return -1; | ||
1473 | } | ||
1474 | break; | ||
1475 | default: | ||
1476 | continue; | ||
1477 | } | ||
1478 | } | ||
1479 | return 0; | ||
1480 | }; | ||
1481 | |||
1482 | RrdGraph.prototype.data_proc = function() | ||
1483 | { | ||
1484 | var pixstep = (this.end - this.start) / this.xsize; | ||
1485 | var paintval; | ||
1486 | var minval = Number.NaN, maxval = Number.NaN; | ||
1487 | var gr_time; | ||
1488 | |||
1489 | /* memory for the processed data */ | ||
1490 | |||
1491 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
1492 | if ((this.gdes[i].gf === RrdGraphDesc.GF_LINE) || (this.gdes[i].gf === RrdGraphDesc.GF_AREA) || (this.gdes[i].gf === RrdGraphDesc.GF_TICK)) { | ||
1493 | this.gdes[i].p_data = []; | ||
1494 | } | ||
1495 | } | ||
1496 | |||
1497 | for (var i = 0; i < this.xsize; i++) { /* for each pixel */ | ||
1498 | var vidx; | ||
1499 | gr_time = this.start + pixstep * i; /* time of the current step */ | ||
1500 | paintval = 0.0; | ||
1501 | |||
1502 | for (var ii = 0 , gdes_c = this.gdes.length; ii < gdes_c; ii++) { | ||
1503 | var value; | ||
1504 | switch (this.gdes[ii].gf) { | ||
1505 | case RrdGraphDesc.GF_LINE: | ||
1506 | case RrdGraphDesc.GF_AREA: | ||
1507 | case RrdGraphDesc.GF_TICK: | ||
1508 | if (!this.gdes[ii].stack) paintval = 0.0; | ||
1509 | value = this.gdes[ii].yrule; | ||
1510 | if (isNaN(value) || (this.gdes[ii].gf === RrdGraphDesc.GF_TICK)) { | ||
1511 | vidx = this.gdes[ii].vidx; | ||
1512 | if (this.gdes[vidx].gf === RrdGraphDesc.GF_VDEF) { | ||
1513 | value = this.gdes[vidx].vf.val; | ||
1514 | } else if (gr_time >= this.gdes[vidx].start && gr_time < this.gdes[vidx].end) { | ||
1515 | value = this.gdes[vidx].data[Math.floor((gr_time - this.gdes[vidx].start) / this.gdes[vidx].step) * this.gdes[vidx].ds_cnt + this.gdes[vidx].ds]; | ||
1516 | } else { | ||
1517 | value = Number.NaN; | ||
1518 | } | ||
1519 | } | ||
1520 | if (!isNaN(value)) { | ||
1521 | paintval += value; | ||
1522 | this.gdes[ii].p_data[i] = paintval; | ||
1523 | if (isFinite(paintval) && this.gdes[ii].gf != RrdGraphDesc.GF_TICK) { | ||
1524 | if ((isNaN(minval) || paintval < minval) && !(this.logarithmic && paintval <= 0.0)) | ||
1525 | minval = paintval; | ||
1526 | if (isNaN(maxval) || paintval > maxval) | ||
1527 | maxval = paintval; | ||
1528 | } | ||
1529 | } else { | ||
1530 | this.gdes[ii].p_data[i] = Number.NaN; | ||
1531 | } | ||
1532 | break; | ||
1533 | case RrdGraphDesc.GF_STACK: | ||
1534 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
1535 | break; | ||
1536 | default: | ||
1537 | break; | ||
1538 | } | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | if (this.logarithmic) { | ||
1543 | if (isNaN(minval) || isNaN(maxval) || maxval <= 0) { | ||
1544 | minval = 0.0; | ||
1545 | maxval = 5.1; | ||
1546 | } | ||
1547 | if (minval <= 0) minval = maxval / 10e8; | ||
1548 | } else { | ||
1549 | if (isNaN(minval) || isNaN(maxval)) { | ||
1550 | minval = 0.0; | ||
1551 | maxval = 1.0; | ||
1552 | } | ||
1553 | } | ||
1554 | |||
1555 | if (isNaN(this.minval) || ((!this.rigid) && this.minval > minval)) { | ||
1556 | if (this.logarithmic) this.minval = minval / 2.0; | ||
1557 | else this.minval = minval; | ||
1558 | } | ||
1559 | if (isNaN(this.maxval) || (!this.rigid && this.maxval < maxval)) { | ||
1560 | if (this.logarithmic) this.maxval = maxval * 2.0; | ||
1561 | else this.maxval = maxval; | ||
1562 | } | ||
1563 | |||
1564 | if (this.minval > this.maxval) { | ||
1565 | if (this.minval > 0) this.minval = 0.99 * this.maxval; | ||
1566 | else this.minval = 1.01 * this.maxval; | ||
1567 | } | ||
1568 | |||
1569 | if (this.AlmostEqual2sComplement(this.minval, this.maxval, 4)) { | ||
1570 | if (this.maxval > 0) this.maxval *= 1.01; | ||
1571 | else this.maxval *= 0.99; | ||
1572 | if (this.AlmostEqual2sComplement(this.maxval, 0, 4)) this.maxval = 1.0; | ||
1573 | } | ||
1574 | return 0; | ||
1575 | }; | ||
1576 | |||
1577 | RrdGraph.prototype.leg_place = function (calc_width) | ||
1578 | { | ||
1579 | var interleg = this.TEXT.LEGEND.size * 1.5; | ||
1580 | var border = this.TEXT.LEGEND.size * 1.5; | ||
1581 | var fill = 0, fill_last; | ||
1582 | var legendwidth; // = this.ximg - 2 * border; | ||
1583 | var leg_c = 0; | ||
1584 | var leg_x = border; | ||
1585 | var leg_y = 0; //this.yimg; | ||
1586 | var leg_y_prev = 0; // this.yimg; | ||
1587 | var leg_cc; | ||
1588 | var glue = 0; | ||
1589 | var ii, mark = 0; | ||
1590 | var default_txtalign = RrdGraphDesc.TXA_JUSTIFIED; /*default line orientation */ | ||
1591 | var legspace; | ||
1592 | var tab; | ||
1593 | var saved_legend; | ||
1594 | |||
1595 | if(calc_width) legendwidth = 0; | ||
1596 | else legendwidth = this.legendwidth - 2 * border; | ||
1597 | |||
1598 | if (!this.no_legend && !this.only_graph) { | ||
1599 | legspace = []; | ||
1600 | for (var i = 0 , gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
1601 | var prt_fctn; /*special printfunctions */ | ||
1602 | if(calc_width) saved_legend = this.gdes[i].legend; | ||
1603 | fill_last = fill; | ||
1604 | if (this.gdes[i].gf === RrdGraphDesc.GF_TEXTALIGN) | ||
1605 | default_txtalign = this.gdes[i].txtalign; | ||
1606 | |||
1607 | if (!this.force_rules_legend) { | ||
1608 | if (this.gdes[i].gf === RrdGraphDesc.GF_HRULE && (this.gdes[i].yrule < this.minval || this.gdes[i].yrule > this.maxval)) | ||
1609 | this.gdes[i].legend = null; | ||
1610 | if (this.gdes[i].gf === RrdGraphDesc.GF_VRULE && (this.gdes[i].xrule < this.start || this.gdes[i].xrule > this.end)) | ||
1611 | this.gdes[i].legend = null; | ||
1612 | } | ||
1613 | this.gdes[i].legend = this.gdes[i].legend.replace(/\\t/gi, "\t") /* turn \\t into tab */ | ||
1614 | |||
1615 | leg_cc = this.gdes[i].legend.length; | ||
1616 | /* is there a controle code at the end of the legend string ? */ | ||
1617 | if (leg_cc >= 2 && this.gdes[i].legend.charAt(leg_cc - 2) === '\\') { | ||
1618 | prt_fctn = this.gdes[i].legend.charAt(leg_cc - 1); | ||
1619 | leg_cc -= 2; | ||
1620 | this.gdes[i].legend = this.gdes[i].legend.substr(0,leg_cc); | ||
1621 | } else { | ||
1622 | prt_fctn = null; | ||
1623 | } | ||
1624 | /* only valid control codes */ | ||
1625 | if (prt_fctn != 'l' && prt_fctn != 'n' && prt_fctn != 'r' && prt_fctn != 'j' && prt_fctn != 'c' && | ||
1626 | prt_fctn != '.' && prt_fctn != 'u' && prt_fctn != 's' && prt_fctn != null && prt_fctn != 'g') { | ||
1627 | throw new RrdGraphError("Unknown control code at the end of "+this.gdes[i].legend+": "+prt_fctn); | ||
1628 | } | ||
1629 | /* \n -> \l */ | ||
1630 | if (prt_fctn === 'n') prt_fctn = 'l'; | ||
1631 | if (prt_fctn === '.') prt_fctn = '\0'; | ||
1632 | |||
1633 | /* remove exess space from the end of the legend for \g */ | ||
1634 | while (prt_fctn === 'g' && leg_cc > 0 && this.gdes[i].legend.charAt(leg_cc - 1) === ' ') { | ||
1635 | leg_cc--; | ||
1636 | this.gdes[i].legend = this.gdes[i].legend.substr(0,leg_cc); | ||
1637 | } | ||
1638 | |||
1639 | if (leg_cc != 0) { | ||
1640 | legspace[i] = (prt_fctn === 'g' ? 0 : interleg); | ||
1641 | if (fill > 0) fill += legspace[i]; | ||
1642 | fill += this.gfx.get_text_width(fill + border, this.TEXT.LEGEND, this.tabwidth, this.gdes[i].legend); | ||
1643 | leg_c++; | ||
1644 | } else { | ||
1645 | legspace[i] = 0; | ||
1646 | } | ||
1647 | /* who said there was a special tag ... ? */ | ||
1648 | if (prt_fctn === 'g') prt_fctn = null; | ||
1649 | |||
1650 | if (prt_fctn === null) { | ||
1651 | if(calc_width && (fill > legendwidth)) legendwidth = fill; | ||
1652 | |||
1653 | if (i === gdes_c - 1 || fill > legendwidth) { | ||
1654 | switch (default_txtalign) { | ||
1655 | case RrdGraphDesc.TXA_RIGHT: | ||
1656 | prt_fctn = 'r'; | ||
1657 | break; | ||
1658 | case RrdGraphDesc.TXA_CENTER: | ||
1659 | prt_fctn = 'c'; | ||
1660 | break; | ||
1661 | case RrdGraphDesc.TXA_JUSTIFIED: | ||
1662 | prt_fctn = 'j'; | ||
1663 | break; | ||
1664 | default: | ||
1665 | prt_fctn = 'l'; | ||
1666 | break; | ||
1667 | } | ||
1668 | } | ||
1669 | /* is it time to place the legends ? */ | ||
1670 | if (fill > legendwidth) { | ||
1671 | if (leg_c > 1) { /* go back one */ | ||
1672 | i--; | ||
1673 | fill = fill_last; | ||
1674 | leg_c--; | ||
1675 | } | ||
1676 | } | ||
1677 | if (leg_c === 1 && prt_fctn === 'j') { | ||
1678 | prt_fctn = 'l'; | ||
1679 | } | ||
1680 | } | ||
1681 | |||
1682 | if (prt_fctn != null) { | ||
1683 | leg_x = border; | ||
1684 | if (leg_c >= 2 && prt_fctn === 'j') { | ||
1685 | glue = (legendwidth - fill) / (leg_c - 1); | ||
1686 | } else { | ||
1687 | glue = 0; | ||
1688 | } | ||
1689 | if (prt_fctn === 'c') | ||
1690 | leg_x = border + (legendwidth - fill) / 2.0; | ||
1691 | if (prt_fctn === 'r') | ||
1692 | leg_x = legendwidth - fill + border; | ||
1693 | for (ii = mark; ii <= i; ii++) { | ||
1694 | if (this.gdes[ii].legend === '') continue; | ||
1695 | this.gdes[ii].leg_x = leg_x; | ||
1696 | this.gdes[ii].leg_y = leg_y + border; | ||
1697 | leg_x += this.gfx.get_text_width(leg_x, this.TEXT.LEGEND, this.tabwidth, this.gdes[ii].legend) + legspace[ii] + glue; | ||
1698 | } | ||
1699 | leg_y_prev = leg_y; | ||
1700 | if (leg_x > border || prt_fctn === 's') leg_y += this.TEXT.LEGEND.size * 1.4; | ||
1701 | if (prt_fctn === 's') leg_y -= this.TEXT.LEGEND.size; | ||
1702 | if (prt_fctn === 'u') leg_y -= this.TEXT.LEGEND.size * 1.4; | ||
1703 | |||
1704 | if(calc_width && (fill > legendwidth)) legendwidth = fill; | ||
1705 | fill = 0; | ||
1706 | leg_c = 0; | ||
1707 | mark = ii; | ||
1708 | } | ||
1709 | |||
1710 | if(calc_width) this.gdes[i].legend = saved_legend; | ||
1711 | } | ||
1712 | if(calc_width) this.legendwidth = legendwidth + 2 * border; | ||
1713 | else this.legendheight = leg_y + border * 0.6; // FIXME 0.6 ?? | ||
1714 | } | ||
1715 | return 0; | ||
1716 | }; | ||
1717 | |||
1718 | RrdGraph.prototype.axis_paint = function() | ||
1719 | { | ||
1720 | this.gfx.line(this.xorigin - 4, this.yorigin, | ||
1721 | this.xorigin + this.xsize + 4, this.yorigin, | ||
1722 | this.MGRIDWIDTH, this.GRC.AXIS); | ||
1723 | |||
1724 | this.gfx.line(this.xorigin, this.yorigin + 4, | ||
1725 | this.xorigin, this.yorigin - this.ysize - 4, | ||
1726 | this.MGRIDWIDTH, this.GRC.AXIS); | ||
1727 | |||
1728 | this.gfx.new_area(this.xorigin + this.xsize + 2, this.yorigin - 3, | ||
1729 | this.xorigin + this.xsize + 2, | ||
1730 | this.yorigin + 3, this.xorigin + this.xsize + 7, this.yorigin, | ||
1731 | this.GRC.ARROW); | ||
1732 | this.gfx.close_path(); | ||
1733 | |||
1734 | this.gfx.new_area(this.xorigin - 3, this.yorigin - this.ysize - 2, | ||
1735 | this.xorigin + 3, this.yorigin - this.ysize - 2, | ||
1736 | this.xorigin, this.yorigin - this.ysize - 7, | ||
1737 | this.GRC.ARROW); | ||
1738 | this.gfx.close_path(); | ||
1739 | |||
1740 | if (this.second_axis_scale != 0){ | ||
1741 | this.gfx.line (this.xorigin+this.xsize,this.yorigin+4, | ||
1742 | this.xorigin+this.xsize,this.yorigin-this.ysize-4, | ||
1743 | MGRIDWIDTH, this.graph_col[this.GRC.AXIS]); | ||
1744 | this.gfx.new_area (this.xorigin+this.xsize-2, this.yorigin-this.ysize-2, | ||
1745 | this.xorigin+this.xsize+3, this.yorigin-this.ysize-2, | ||
1746 | this.xorigin+this.xsize, this.yorigin-this.ysize-7, /* LINEOFFSET */ | ||
1747 | this.GRC.ARROW); | ||
1748 | this.gfx.close_path(); | ||
1749 | } | ||
1750 | }; | ||
1751 | |||
1752 | RrdGraph.prototype.frexp10 = function (x) | ||
1753 | { | ||
1754 | var mnt; | ||
1755 | var iexp; | ||
1756 | |||
1757 | iexp = Math.floor(Math.log(Math.abs(x)) / Math.LN10); | ||
1758 | mnt = x / Math.pow(10.0, iexp); | ||
1759 | if (mnt >= 10.0) { | ||
1760 | iexp++; | ||
1761 | mnt = x / Math.pow(10.0, iexp); | ||
1762 | } | ||
1763 | return [mnt, iexp]; | ||
1764 | }; | ||
1765 | |||
1766 | RrdGraph.prototype.horizontal_log_grid = function () | ||
1767 | { | ||
1768 | var yloglab = [ [ 1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
1769 | [ 1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
1770 | [ 1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
1771 | [ 1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0 ], | ||
1772 | [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10. ], | ||
1773 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] /* last line */ | ||
1774 | ]; | ||
1775 | var i, j, val_exp, min_exp; | ||
1776 | var nex; /* number of decades in data */ | ||
1777 | var logscale; /* scale in logarithmic space */ | ||
1778 | var exfrac = 1; /* decade spacing */ | ||
1779 | var mid = -1; /* row in yloglab for major grid */ | ||
1780 | var mspac; /* smallest major grid spacing (pixels) */ | ||
1781 | var flab; /* first value in yloglab to use */ | ||
1782 | var value, tmp, pre_value; | ||
1783 | var X0, X1, Y0; | ||
1784 | var graph_label; | ||
1785 | |||
1786 | nex = Math.log(this.maxval / this.minval)/Math.LN10; | ||
1787 | logscale = this.ysize / nex; | ||
1788 | /* major spacing for data with high dynamic range */ | ||
1789 | while (logscale * exfrac < 2.3 * this.TEXT.LEGEND.size) { // FIXME 3 -> 2.34 ?? | ||
1790 | if (exfrac === 1) exfrac = 2.3; // ??? 3 -> 2.34 | ||
1791 | else exfrac += 2.3 ; // 3-> 2.34 | ||
1792 | } | ||
1793 | /* major spacing for less dynamic data */ | ||
1794 | do { | ||
1795 | mid++; | ||
1796 | for (i = 0; yloglab[mid][i + 1] < 10.0; i++) {}; | ||
1797 | mspac = logscale * Math.log(10.0 / yloglab[mid][i])/Math.LN10; | ||
1798 | } while (mspac > 1.56 * this.TEXT.LEGEND.size && yloglab[mid][0] > 0); // FIXME 2->1.56 ?? | ||
1799 | if (mid) mid--; | ||
1800 | /* find first value in yloglab */ | ||
1801 | //for (flab = 0; yloglab[mid][flab] < 10 && this.frexp10(this.minval,tmp) > yloglab[mid][flab]; flab++); | ||
1802 | flab = -1; | ||
1803 | do { | ||
1804 | var ret; | ||
1805 | flab++; | ||
1806 | // [ret, tmp] = this.frexp10(this.minval); | ||
1807 | var dummy = this.frexp10(this.minval); ret = dummy[0]; tmp = dummy[1]; | ||
1808 | } while (yloglab[mid][flab] < 10 && ret > yloglab[mid][flab]); | ||
1809 | |||
1810 | if (yloglab[mid][flab] === 10.0) { | ||
1811 | tmp += 1.0; | ||
1812 | flab = 0; | ||
1813 | } | ||
1814 | |||
1815 | val_exp = tmp; | ||
1816 | if (val_exp % exfrac) val_exp += Math.abs(-val_exp % exfrac); | ||
1817 | X0 = this.xorigin; | ||
1818 | X1 = this.xorigin + this.xsize; | ||
1819 | |||
1820 | /* draw grid */ | ||
1821 | pre_value = Number.NAN; | ||
1822 | |||
1823 | while (1) { | ||
1824 | value = yloglab[mid][flab] * Math.pow(10.0, val_exp); | ||
1825 | if (this.AlmostEqual2sComplement(value, pre_value, 4)) // FIXME | ||
1826 | break; /* it seems we are not converging */ | ||
1827 | pre_value = value; | ||
1828 | Y0 = this.ytr(value); | ||
1829 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) | ||
1830 | break; | ||
1831 | /* major grid line */ | ||
1832 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
1833 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
1834 | this.gfx.dashed_line(X0 - 2, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
1835 | /* label */ | ||
1836 | if (this.force_units_si) { | ||
1837 | var scale; | ||
1838 | var pvalue; | ||
1839 | var symbol; | ||
1840 | |||
1841 | scale = Math.floor(val_exp / 3.0); | ||
1842 | if (value >= 1.0) pvalue = Math.pow(10.0, val_exp % 3); | ||
1843 | else pvalue = Math.pow(10.0, ((val_exp + 1) % 3) + 2); | ||
1844 | pvalue *= yloglab[mid][flab]; | ||
1845 | |||
1846 | if (((scale + this.si_symbcenter) < this.si_symbol.length) && ((scale + this.si_symbcenter) >= 0)) | ||
1847 | symbol = this.si_symbol[scale + this.si_symbcenter]; | ||
1848 | else | ||
1849 | symbol = '?'; | ||
1850 | graph_label = sprintf("%3.0f %s", pvalue, symbol); | ||
1851 | } else { | ||
1852 | graph_label = sprintf("%3.0e", value); | ||
1853 | } | ||
1854 | if (this.second_axis_scale != 0){ | ||
1855 | var graph_label_right; | ||
1856 | var sval = value*this.second_axis_scale+this.second_axis_shift; | ||
1857 | if (!this.second_axis_format[0]){ | ||
1858 | if (this.force_units_si) { | ||
1859 | var mfac = 1; | ||
1860 | var symb = ''; | ||
1861 | //[sval, symb, mfac ] = this.auto_scale(sval, symb, mfac); | ||
1862 | var dummy = this.auto_scale(sval, symb, mfac); sval = dummy[0]; symb = dummy[1]; mfac = dummy[2]; | ||
1863 | graph_label_right = sprintf("%4.0f %s", sval,symb); | ||
1864 | } else { | ||
1865 | graph_label_right = sprintf("%3.0e", sval); | ||
1866 | } | ||
1867 | } else { | ||
1868 | graph_label_right = sprintf(this.second_axis_format,sval,""); | ||
1869 | } | ||
1870 | this.gfx.text( X1+7, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth,0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_CENTER, graph_label_right ); | ||
1871 | } | ||
1872 | |||
1873 | this.gfx.text(X0 - this.TEXT.AXIS.size, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth, 0.0, RrdGraph.GFX_H_RIGHT, RrdGraph.GFX_V_CENTER, graph_label); | ||
1874 | if (mid < 4 && exfrac === 1) { /* minor grid */ | ||
1875 | if (flab === 0) { /* find first and last minor line behind current major line * i is the first line and j tha last */ | ||
1876 | min_exp = val_exp - 1; | ||
1877 | for (i = 1; yloglab[mid][i] < 10.0; i++) {}; | ||
1878 | i = yloglab[mid][i - 1] + 1; | ||
1879 | j = 10; | ||
1880 | } else { | ||
1881 | min_exp = val_exp; | ||
1882 | i = yloglab[mid][flab - 1] + 1; | ||
1883 | j = yloglab[mid][flab]; | ||
1884 | } | ||
1885 | for (; i < j; i++) { /* draw minor lines below current major line */ | ||
1886 | value = i * Math.pow(10.0, min_exp); | ||
1887 | if (value < this.minval) continue; | ||
1888 | Y0 = this.ytr(value); | ||
1889 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
1890 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1891 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1892 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
1893 | } | ||
1894 | } else if (exfrac > 1) { | ||
1895 | for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { | ||
1896 | value = Math.pow(10.0, i); | ||
1897 | if (value < this.minval) continue; | ||
1898 | Y0 = this.ytr(value); | ||
1899 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
1900 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1901 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1902 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
1903 | } | ||
1904 | } | ||
1905 | if (yloglab[mid][++flab] === 10.0) { /* next decade */ | ||
1906 | flab = 0; | ||
1907 | val_exp += exfrac; | ||
1908 | } | ||
1909 | } | ||
1910 | if (mid < 4 && exfrac === 1) { /* draw minor lines after highest major line */ | ||
1911 | if (flab === 0) { /* find first and last minor line below current major line * i is the first line and j tha last */ | ||
1912 | min_exp = val_exp - 1; | ||
1913 | for (i = 1; yloglab[mid][i] < 10.0; i++) {}; | ||
1914 | i = yloglab[mid][i - 1] + 1; | ||
1915 | j = 10; | ||
1916 | } else { | ||
1917 | min_exp = val_exp; | ||
1918 | i = yloglab[mid][flab - 1] + 1; | ||
1919 | j = yloglab[mid][flab]; | ||
1920 | } | ||
1921 | for (; i < j; i++) { /* draw minor lines below current major line */ | ||
1922 | value = i * Math.pow(10.0, min_exp); | ||
1923 | if (value < this.minval) continue; | ||
1924 | Y0 = this.ytr(value); | ||
1925 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
1926 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1927 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1928 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
1929 | } | ||
1930 | } else if (exfrac > 1) { /* fancy minor gridlines */ | ||
1931 | for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { | ||
1932 | value = Math.pow(10.0, i); | ||
1933 | if (value < this.minval) continue; | ||
1934 | Y0 = this.ytr(value); | ||
1935 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
1936 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1937 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
1938 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
1939 | } | ||
1940 | } | ||
1941 | return 1; | ||
1942 | }; | ||
1943 | |||
1944 | RrdGraph.prototype.vertical_grid = function() | ||
1945 | { | ||
1946 | var xlab_sel; /* which sort of label and grid ? */ | ||
1947 | var ti, tilab, timajor; | ||
1948 | var factor; | ||
1949 | var graph_label; | ||
1950 | var X0, Y0, Y1; /* points for filled graph and more */ | ||
1951 | |||
1952 | /* the type of time grid is determined by finding the number of seconds per pixel in the graph */ | ||
1953 | if (this.xlab_user.minsec === -1) { | ||
1954 | factor = (this.end - this.start) / this.xsize; | ||
1955 | xlab_sel = 0; | ||
1956 | |||
1957 | while (this.xlab[xlab_sel + 1].minsec != -1 && this.xlab[xlab_sel + 1].minsec <= factor) xlab_sel++; | ||
1958 | if (xlab_sel === 0) xlab_sel=1; // FIXME XXX XXX xlab_sel == 0 ??? | ||
1959 | while (this.xlab[xlab_sel - 1].minsec === this.xlab[xlab_sel].minsec && this.xlab[xlab_sel].length > (this.end - this.start)) xlab_sel--; | ||
1960 | this.xlab_user = this.xlab[xlab_sel]; | ||
1961 | |||
1962 | } | ||
1963 | Y0 = this.yorigin; | ||
1964 | Y1 = this.yorigin - this.ysize; | ||
1965 | |||
1966 | if (!(this.no_minor)) { | ||
1967 | for ( ti = this.find_first_time(this.start, this.xlab_user.gridtm, this.xlab_user.gridst), | ||
1968 | timajor = this.find_first_time(this.start, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
1969 | ti < this.end && ti != -1; | ||
1970 | ti = this.find_next_time(ti, this.xlab_user.gridtm, this.xlab_user.gridst)) { | ||
1971 | if (ti < this.start || ti > this.end) continue; | ||
1972 | while (timajor < ti && timajor != -1) timajor = this.find_next_time(timajor, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
1973 | if (timajor === -1) break; | ||
1974 | if (ti === timajor) continue; | ||
1975 | X0 = this.xtr(ti); | ||
1976 | this.gfx.line(X0, Y1 - 2, X0, Y1, this.GRIDWIDTH, this.GRC.GRID); | ||
1977 | this.gfx.line(X0, Y0, X0, Y0 + 2, this.GRIDWIDTH, this.GRC.GRID); | ||
1978 | this.gfx.dashed_line(X0, Y0 + 1, X0, Y1 - 1, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
1979 | } | ||
1980 | } | ||
1981 | |||
1982 | for ( ti = this.find_first_time(this.start, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
1983 | ti < this.end && ti != -1; | ||
1984 | ti = this.find_next_time(ti, this.xlab_user.mgridtm, this.xlab_user.mgridst) | ||
1985 | ) { | ||
1986 | if (ti < this.start || ti > this.end) continue; | ||
1987 | X0 = this.xtr(ti); | ||
1988 | this.gfx.line(X0, Y1 - 2, X0, Y1, this.MGRIDWIDTH, this.GRC.MGRID); | ||
1989 | this.gfx.line(X0, Y0, X0, Y0 + 3, this.MGRIDWIDTH, this.GRC.MGRID); | ||
1990 | this.gfx.dashed_line(X0, Y0 + 3, X0, Y1 - 2, this.MGRIDWIDTH, | ||
1991 | this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
1992 | } | ||
1993 | |||
1994 | for ( ti = this.find_first_time(this.start - this.xlab_user.precis / 2, this.xlab_user.labtm, this.xlab_user.labst); | ||
1995 | (ti <= this.end - this.xlab_user.precis / 2) && ti != -1; | ||
1996 | ti = this.find_next_time(ti, this.xlab_user.labtm, this.xlab_user.labst) | ||
1997 | ) { | ||
1998 | tilab = ti + this.xlab_user.precis / 2; | ||
1999 | if (tilab < this.start || tilab > this.end) | ||
2000 | continue; | ||
2001 | //localtime_r(&tilab, &tm); FIXME | ||
2002 | //strftime(graph_label, 99, this.xlab_user.stst, &tm); | ||
2003 | graph_label = strftime(this.xlab_user.stst, tilab); | ||
2004 | this.gfx.text(this.xtr(tilab), Y0 + 3, this.GRC.FONT, | ||
2005 | this.TEXT.AXIS, this.tabwidth, 0.0, | ||
2006 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_TOP, graph_label); | ||
2007 | } | ||
2008 | }; | ||
2009 | |||
2010 | RrdGraph.prototype.auto_scale = function (value, symb_ptr, magfact) | ||
2011 | { | ||
2012 | var sindex; | ||
2013 | |||
2014 | if (value === 0.0 || isNaN(value)) { | ||
2015 | sindex = 0; | ||
2016 | magfact = 1.0; | ||
2017 | } else { | ||
2018 | sindex = Math.floor((Math.log(Math.abs(value))/Math.LN10) / (Math.log(this.base)/Math.LN10)); | ||
2019 | magfact = Math.pow(this.base, sindex); | ||
2020 | value /= magfact; | ||
2021 | } | ||
2022 | if (sindex <= this.si_symbcenter && sindex >= -this.si_symbcenter) { | ||
2023 | symb_ptr = this.si_symbol[sindex + this.si_symbcenter]; | ||
2024 | } else { | ||
2025 | symb_ptr = '?'; | ||
2026 | } | ||
2027 | return [value, symb_ptr, magfact]; | ||
2028 | }; | ||
2029 | |||
2030 | RrdGraph.prototype.si_unit = function() | ||
2031 | { | ||
2032 | var digits; | ||
2033 | var viewdigits = 0; | ||
2034 | |||
2035 | digits = Math.floor(Math.log(Math.max(Math.abs(this.minval), Math.abs(this.maxval))) / Math.log(this.base)); | ||
2036 | |||
2037 | if (this.unitsexponent != 9999) { | ||
2038 | /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */ | ||
2039 | viewdigits = Math.floor(this.unitsexponent / 3); | ||
2040 | } else { | ||
2041 | viewdigits = digits; | ||
2042 | } | ||
2043 | |||
2044 | this.magfact = Math.pow(this.base, digits); | ||
2045 | |||
2046 | this.viewfactor = this.magfact / Math.pow(this.base, viewdigits); | ||
2047 | |||
2048 | if (((viewdigits + this.si_symbcenter) < this.si_symbol.length) && ((viewdigits + this.si_symbcenter) >= 0)) | ||
2049 | this.symbol = this.si_symbol[viewdigits + this.si_symbcenter]; | ||
2050 | else | ||
2051 | this.symbol = '?'; | ||
2052 | }; | ||
2053 | |||
2054 | RrdGraph.prototype.expand_range = function () | ||
2055 | { | ||
2056 | var sensiblevalues = [ 1000.0, 900.0, 800.0, 750.0, 700.0, 600.0, 500.0, 400.0, 300.0, 250.0, 200.0, 125.0, 100.0, 90.0, 80.0, 75.0, 70.0, 60.0, 50.0, 40.0, 30.0, 25.0, 20.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.8, 1.5, 1.2, 1.0, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1 ]; | ||
2057 | var scaled_min, scaled_max; | ||
2058 | var adj; | ||
2059 | var i; | ||
2060 | |||
2061 | if (isNaN(this.ygridstep)) { | ||
2062 | if (this.alt_autoscale) { | ||
2063 | var delt, fact; | ||
2064 | |||
2065 | delt = this.maxval - this.minval; | ||
2066 | adj = delt * 0.1; | ||
2067 | fact = 2.0 * Math.pow(10.0, Math.floor(Math.log(Math.max(Math.abs(this.minval), Math.abs(this.maxval)) / this.magfact)/Math.LN10) - 2); | ||
2068 | if (delt < fact) adj = (fact - delt) * 0.55; | ||
2069 | this.minval -= adj; | ||
2070 | this.maxval += adj; | ||
2071 | } else if (this.alt_autoscale_min) { | ||
2072 | adj = (this.maxval - this.minval) * 0.1; | ||
2073 | this.minval -= adj; | ||
2074 | } else if (this.alt_autoscale_max) { | ||
2075 | adj = (this.maxval - this.minval) * 0.1; | ||
2076 | this.maxval += adj; | ||
2077 | } else { | ||
2078 | scaled_min = this.minval / this.magfact; | ||
2079 | scaled_max = this.maxval / this.magfact; | ||
2080 | |||
2081 | for (i = 1; sensiblevalues[i] > 0; i++) { | ||
2082 | if (sensiblevalues[i - 1] >= scaled_min && sensiblevalues[i] <= scaled_min) | ||
2083 | this.minval = sensiblevalues[i] * this.magfact; | ||
2084 | if (-sensiblevalues[i - 1] <= scaled_min && -sensiblevalues[i] >= scaled_min) | ||
2085 | this.minval = -sensiblevalues[i - 1] * this.magfact; | ||
2086 | if (sensiblevalues[i - 1] >= scaled_max && sensiblevalues[i] <= scaled_max) | ||
2087 | this.maxval = sensiblevalues[i - 1] * this.magfact; | ||
2088 | if (-sensiblevalues[i - 1] <= scaled_max && -sensiblevalues[i] >= scaled_max) | ||
2089 | this.maxval = -sensiblevalues[i] * this.magfact; | ||
2090 | } | ||
2091 | } | ||
2092 | } else { | ||
2093 | this.minval = this.ylabfact * this.ygridstep * Math.floor(this.minval / (this.ylabfact * this.ygridstep)); | ||
2094 | this.maxval = this.ylabfact * this.ygridstep * Math.ceil(this.maxval / (this.ylabfact * this.ygridstep)); | ||
2095 | } | ||
2096 | }; | ||
2097 | |||
2098 | RrdGraph.prototype.calc_horizontal_grid = function() | ||
2099 | { | ||
2100 | var range; | ||
2101 | var scaledrange; | ||
2102 | var pixel, i; | ||
2103 | var gridind = 0; | ||
2104 | var decimals, fractionals; | ||
2105 | |||
2106 | this.ygrid_scale.labfact = 2; | ||
2107 | range = this.maxval - this.minval; | ||
2108 | scaledrange = range / this.magfact; | ||
2109 | if (isNaN(scaledrange) || !isFinite(scaledrange)) { | ||
2110 | return false; | ||
2111 | } | ||
2112 | |||
2113 | pixel = 1; | ||
2114 | if (isNaN(this.ygridstep)) { | ||
2115 | if (this.alt_ygrid) { | ||
2116 | decimals = Math.ceil(Math.log(Math.max(Math.abs(this.maxval), Math.abs(this.minval)) * this.viewfactor / this.magfact)/Math.LN10); | ||
2117 | if (decimals <= 0) decimals = 1; | ||
2118 | this.ygrid_scale.gridstep = Math.pow(10, Math.floor(Math.log(range * this.viewfactor / this.magfact)/Math.LN10)) / this.viewfactor * this.magfact; | ||
2119 | if (this.ygrid_scale.gridstep === 0) this.ygrid_scale.gridstep = 0.1; | ||
2120 | if (range / this.ygrid_scale.gridstep < 5 && this.ygrid_scale.gridstep >= 30) | ||
2121 | this.ygrid_scale.gridstep /= 10; | ||
2122 | if (range / this.ygrid_scale.gridstep > 15) | ||
2123 | this.ygrid_scale.gridstep *= 10; | ||
2124 | if (range / this.ygrid_scale.gridstep > 5) { | ||
2125 | this.ygrid_scale.labfact = 1; | ||
2126 | if (range / this.ygrid_scale.gridstep > 8 || this.ygrid_scale.gridstep < 1.8 * this.TEXT.AXIS.size) // 1.8 | ||
2127 | this.ygrid_scale.labfact = 2; | ||
2128 | } else { | ||
2129 | this.ygrid_scale.gridstep /= 5; | ||
2130 | this.ygrid_scale.labfact = 5; | ||
2131 | } | ||
2132 | |||
2133 | fractionals = Math.floor(Math.log(this.ygrid_scale.gridstep * this.ygrid_scale.labfact * this.viewfactor / this.magfact)/Math.LN10); | ||
2134 | if (fractionals < 0) { /* small amplitude. */ | ||
2135 | var len = decimals - fractionals + 1; | ||
2136 | if (this.unitslength < len + 2) this.unitslength = len + 2; | ||
2137 | this.ygrid_scale.labfmt = sprintf("%%%d.%df%s", len, -fractionals, (this.symbol != ' ' ? " %s" : "")); | ||
2138 | } else { | ||
2139 | var len = decimals + 1; | ||
2140 | if (this.unitslength < len + 2) this.unitslength = len + 2; | ||
2141 | this.ygrid_scale.labfmt = sprintf("%%%d.0f%s", len, (this.symbol != ' ' ? " %s" : "")); | ||
2142 | } | ||
2143 | } else { /* classic rrd grid */ | ||
2144 | for (i = 0; this.ylab[i].grid > 0; i++) { | ||
2145 | pixel = this.ysize / (scaledrange / this.ylab[i].grid); | ||
2146 | gridind = i; | ||
2147 | if (pixel >= 5) break; | ||
2148 | } | ||
2149 | for (i = 0; i < 4; i++) { | ||
2150 | if (pixel * this.ylab[gridind].lfac[i] >= 1.8 * this.TEXT.AXIS.size) { // 1.8 | ||
2151 | this.ygrid_scale.labfact = this.ylab[gridind].lfac[i]; | ||
2152 | break; | ||
2153 | } | ||
2154 | } | ||
2155 | this.ygrid_scale.gridstep = this.ylab[gridind].grid * this.magfact; | ||
2156 | } | ||
2157 | } else { | ||
2158 | this.ygrid_scale.gridstep = this.ygridstep; | ||
2159 | this.ygrid_scale.labfact = this.ylabfact; | ||
2160 | } | ||
2161 | return true; | ||
2162 | }; | ||
2163 | |||
2164 | RrdGraph.prototype.draw_horizontal_grid = function() | ||
2165 | { | ||
2166 | var i; | ||
2167 | var scaledstep; | ||
2168 | var graph_label; | ||
2169 | var nlabels = 0; | ||
2170 | var X0 = this.xorigin; | ||
2171 | var X1 = this.xorigin + this.xsize; | ||
2172 | var sgrid = Math.round(this.minval / this.ygrid_scale.gridstep - 1); | ||
2173 | var egrid = Math.round(this.maxval / this.ygrid_scale.gridstep + 1); | ||
2174 | var MaxY; | ||
2175 | var second_axis_magfact = 0; | ||
2176 | var second_axis_symb = ''; | ||
2177 | var Y0, YN; | ||
2178 | var sisym; | ||
2179 | |||
2180 | scaledstep = this.ygrid_scale.gridstep / this.magfact * this.viewfactor; | ||
2181 | MaxY = scaledstep * egrid; | ||
2182 | for (i = sgrid; i <= egrid; i++) { | ||
2183 | Y0 = this.ytr(this.ygrid_scale.gridstep * i); | ||
2184 | YN = this.ytr(this.ygrid_scale.gridstep * (i + 1)); | ||
2185 | if (Math.floor(Y0 + 0.5) >= this.yorigin - this.ysize && Math.floor(Y0 + 0.5) <= this.yorigin) { | ||
2186 | if (i % this.ygrid_scale.labfact === 0 || (nlabels === 1 && (YN < this.yorigin - this.ysize || YN > this.yorigin))) { | ||
2187 | if (this.symbol === ' ') { | ||
2188 | if (this.alt_ygrid) { | ||
2189 | graph_label = sprintf(this.ygrid_scale.labfmt, scaledstep * i); // FIXME | ||
2190 | } else { | ||
2191 | if (MaxY < 10) { | ||
2192 | graph_label = sprintf("%4.1f", scaledstep * i); | ||
2193 | } else { | ||
2194 | graph_label = sprintf("%4.0f", scaledstep * i); | ||
2195 | } | ||
2196 | } | ||
2197 | } else { | ||
2198 | sisym = (i === 0 ? ' ' : this.symbol); | ||
2199 | if (this.alt_ygrid) { | ||
2200 | graph_label = sprintf(this.ygrid_scale.labfmt, scaledstep * i, sisym); | ||
2201 | } else { | ||
2202 | if (MaxY < 10) { | ||
2203 | graph_label = sprintf("%4.1f %s", scaledstep * i, sisym); | ||
2204 | } else { | ||
2205 | graph_label = sprintf("%4.0f %s", scaledstep * i, sisym); | ||
2206 | } | ||
2207 | } | ||
2208 | } | ||
2209 | nlabels++; | ||
2210 | if (this.second_axis_scale != 0){ | ||
2211 | var graph_label_right; | ||
2212 | sval = this.ygrid_scale.gridstep*i*this.second_axis_scale+this.second_axis_shift; | ||
2213 | if (!this.second_axis_format){ | ||
2214 | if (!second_axis_magfact){ | ||
2215 | var dummy = this.ygrid_scale.gridstep*(sgrid+egrid)/2.0*this.second_axis_scale+this.second_axis_shift; | ||
2216 | //[dummy, second_axis_symb, second_axis_magfact ] = this.auto_scale(dummy,second_axis_symb,second_axis_magfact); | ||
2217 | dummy = this.auto_scale(dummy,second_axis_symb,second_axis_magfact); second_axis_symb = dummy[1]; second_axis_magfact=dummy[2]; | ||
2218 | } | ||
2219 | sval /= second_axis_magfact; | ||
2220 | if(MaxY < 10) { | ||
2221 | graph_label_right = sprintf("%5.1f %s", sval, second_axis_symb); | ||
2222 | } else { | ||
2223 | graph_label_right = sprintf("%5.0f %s", sval, second_axis_symb); | ||
2224 | } | ||
2225 | } else { | ||
2226 | graph_label_right = sprintf(this.second_axis_format, sval); | ||
2227 | } | ||
2228 | this.gfx.text (X1+7, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth, 0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_CENTER, graph_label_right ); | ||
2229 | } | ||
2230 | this.gfx.text(X0 - this.TEXT.AXIS.size , Y0, this.GRC.FONT, this.TEXT.AXIS , this.tabwidth, 0.0, RrdGraph.GFX_H_RIGHT, RrdGraph.GFX_V_CENTER, graph_label); | ||
2231 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
2232 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
2233 | this.gfx.dashed_line(X0 - 2, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
2234 | } else if (!this.no_minor) { | ||
2235 | this.gfx.line( X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
2236 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
2237 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
2238 | } | ||
2239 | } | ||
2240 | } | ||
2241 | return 1; | ||
2242 | }; | ||
2243 | |||
2244 | RrdGraph.prototype.grid_paint = function() | ||
2245 | { | ||
2246 | var i; | ||
2247 | var res = 0; | ||
2248 | var X0, Y0; | ||
2249 | |||
2250 | if (this.draw_3d_border > 0) { | ||
2251 | i = this.draw_3d_border; | ||
2252 | this.gfx.new_area(0, this.yimg, i, this.yimg - i, i, i, this.GRC.SHADEA); | ||
2253 | this.gfx.add_point(this.ximg - i, i); | ||
2254 | this.gfx.add_point(this.ximg, 0); | ||
2255 | this.gfx.add_point(0, 0); | ||
2256 | this.gfx.close_path(); | ||
2257 | this.gfx.new_area(i, this.yimg - i, this.ximg - i, this.yimg - i, this.ximg - i, i, this.GRC.SHADEB); | ||
2258 | this.gfx.add_point(this.ximg, 0); | ||
2259 | this.gfx.add_point(this.ximg, this.yimg); | ||
2260 | this.gfx.add_point(0, this.yimg); | ||
2261 | this.gfx.close_path(); | ||
2262 | } | ||
2263 | if (this.draw_x_grid) | ||
2264 | this.vertical_grid(); | ||
2265 | if (this.draw_y_grid) { | ||
2266 | if (this.logarithmic) | ||
2267 | res = this.horizontal_log_grid(); | ||
2268 | else | ||
2269 | res = this.draw_horizontal_grid(); | ||
2270 | /* dont draw horizontal grid if there is no min and max val */ | ||
2271 | if (!res) { | ||
2272 | this.gfx.text(this.ximg / 2, (2 * this.yorigin - this.ysize) / 2, | ||
2273 | this.GRC.FONT, this.TEXT.AXIS, | ||
2274 | this.tabwidth, 0.0, | ||
2275 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, 'No Data found'); | ||
2276 | } | ||
2277 | } | ||
2278 | |||
2279 | /* yaxis unit description */ | ||
2280 | if (this.ylegend){ | ||
2281 | this.gfx.text(this.xOriginLegendY+10, this.yOriginLegendY, | ||
2282 | this.GRC.FONT, this.TEXT.UNIT, this.tabwidth, this.YLEGEND_ANGLE, | ||
2283 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, this.ylegend); | ||
2284 | |||
2285 | } | ||
2286 | if (this.second_axis_legend){ | ||
2287 | this.gfx.text(this.xOriginLegendY2+10, this.yOriginLegendY2, | ||
2288 | this.GRC.FONT, this.TEXT.UNIT, this.tabwidth, this.YLEGEND_ANGLE, | ||
2289 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, this.second_axis_legend); | ||
2290 | } | ||
2291 | |||
2292 | /* graph title */ | ||
2293 | this.gfx.text(this.xOriginTitle, this.yOriginTitle+6, | ||
2294 | this.GRC.FONT, this.TEXT.TITLE, this.tabwidth, 0.0, RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_TOP, this.title); | ||
2295 | /* rrdtool 'logo' */ | ||
2296 | if (!this.no_rrdtool_tag){ | ||
2297 | var color = this.parse_color(this.GRC.FONT); | ||
2298 | color[3] = 0.3; | ||
2299 | var water_color = this.color2rgba(color); | ||
2300 | var xpos = this.legendposition === RrdGraph.LEGEND_POS_EAST ? this.xOriginLegendY : this.ximg - 4; | ||
2301 | this.gfx.text(xpos, 5, water_color, this.TEXT.WATERMARK, this.tabwidth, | ||
2302 | -90, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); | ||
2303 | } | ||
2304 | /* graph watermark */ | ||
2305 | if (this.watermark) { | ||
2306 | var color = this.parse_color(this.GRC.FONT) | ||
2307 | color[3] = 0.3; | ||
2308 | var water_color = this.color2rgba(color); | ||
2309 | this.gfx.text(this.ximg / 2, this.yimg - 6, water_color, this.TEXT.WATERMARK , this.tabwidth, 0, | ||
2310 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_BOTTOM, this.watermark); | ||
2311 | } | ||
2312 | /* graph labels */ | ||
2313 | if (!(this.no_legend) && !(this.only_graph)) { | ||
2314 | for (var i = 0 , gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
2315 | if (!this.gdes[i].legend) continue; | ||
2316 | X0 = this.xOriginLegend + this.gdes[i].leg_x; | ||
2317 | Y0 = this.legenddirection === RrdGraph.LEGEND_DIR_TOP_DOWN ? this.yOriginLegend + this.gdes[i].leg_y : this.yOriginLegend + this.legendheight - this.gdes[i].leg_y; | ||
2318 | this.gfx.text(X0, Y0, this.GRC.FONT, this.TEXT.LEGEND, this.tabwidth, 0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_BOTTOM, this.gdes[i].legend); | ||
2319 | if (this.gdes[i].gf != RrdGraphDesc.GF_PRINT && this.gdes[i].gf != RrdGraphDesc.GF_GPRINT && this.gdes[i].gf != RrdGraphDesc.GF_COMMENT) { | ||
2320 | var boxH, boxV; | ||
2321 | var X1, Y1; | ||
2322 | |||
2323 | boxH = this.gfx.get_text_width(0,this.TEXT.LEGEND, this.tabwidth, 'o') * 1.2; | ||
2324 | boxV = boxH; | ||
2325 | |||
2326 | Y0 -= boxV * 0.4; | ||
2327 | |||
2328 | if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_HRULE) { | ||
2329 | this.gfx.line(X0, Y0 - boxV / 2, X0 + boxH, Y0 - boxV / 2, 1.0, this.gdes[i].col); | ||
2330 | } else if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_VRULE) { | ||
2331 | this.gfx.line(X0 + boxH / 2, Y0, X0 + boxH / 2, Y0 - boxV, 1.0, this.gdes[i].col); | ||
2332 | } else if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_LINE) { | ||
2333 | this.gfx.line(X0, Y0, X0 + boxH, Y0 - boxV, this.gdes[i].linewidth, this.gdes[i].col); | ||
2334 | } else { | ||
2335 | this.gfx.new_area(X0, Y0 - boxV, X0, Y0, X0 + boxH, Y0, this.GRC.BACK); | ||
2336 | this.gfx.add_point(X0 + boxH, Y0 - boxV); | ||
2337 | this.gfx.close_path(); | ||
2338 | this.gfx.new_area(X0, Y0 - boxV, X0, Y0, X0 + boxH, Y0, this.gdes[i].col); | ||
2339 | this.gfx.add_point(X0 + boxH, Y0 - boxV); | ||
2340 | this.gfx.close_path(); | ||
2341 | if (this.gdes[i].dash) this.gfx.set_dash([ 3.0 ], 1, 0.0); | ||
2342 | this.gfx.rectangle(X0, Y0, X0 + boxH, Y0 - boxV, 1.0, this.GRC.FRAME); | ||
2343 | } | ||
2344 | } | ||
2345 | } | ||
2346 | } | ||
2347 | }; | ||
2348 | |||
2349 | RrdGraph.prototype.graph_size_location = function (elements) | ||
2350 | { | ||
2351 | var Xvertical = 0; | ||
2352 | var Xvertical2 = 0; | ||
2353 | var Ytitle = 0; | ||
2354 | var Xylabel = 0; | ||
2355 | var Xmain = 0; | ||
2356 | var Ymain = 0; | ||
2357 | var Yxlabel = 0; | ||
2358 | var Xspacing = 15; | ||
2359 | var Yspacing = 15; | ||
2360 | var Ywatermark = 4; | ||
2361 | |||
2362 | if (this.only_graph) { | ||
2363 | this.xorigin = 0; | ||
2364 | this.ximg = this.xsize; | ||
2365 | this.yimg = this.ysize; | ||
2366 | this.yorigin = this.ysize; | ||
2367 | this.xtr(0); | ||
2368 | this.ytr(Number.NaN); | ||
2369 | return 0; | ||
2370 | } | ||
2371 | |||
2372 | if(this.watermark) | ||
2373 | Ywatermark = this.TEXT.WATERMARK.size * 1.5; // 2 | ||
2374 | if(this.ylegend) | ||
2375 | Xvertical = this.TEXT.UNIT.size * 1.5; // 2 | ||
2376 | if(this.second_axis_legend) { | ||
2377 | Xvertical2 = this.TEXT.UNIT.size * 1.5; // 2 | ||
2378 | } else { | ||
2379 | Xvertical2 = Xspacing; | ||
2380 | } | ||
2381 | |||
2382 | if(this.title) | ||
2383 | Ytitle = this.TEXT.TITLE.size * 1.95 + 10; // 2.6 | ||
2384 | else | ||
2385 | Ytitle = Yspacing; | ||
2386 | |||
2387 | if (elements) { | ||
2388 | if (this.draw_x_grid) | ||
2389 | Yxlabel = this.TEXT.AXIS.size * 1.35; // 2.5 1.87 | ||
2390 | if (this.draw_y_grid || this.forceleftspace) // FIXME | ||
2391 | Xylabel = this.gfx.get_text_width(0, this.TEXT.AXIS, this.tabwidth, '0') * this.unitslength; | ||
2392 | } | ||
2393 | Xylabel += Xspacing; | ||
2394 | this.legendheight = 0; | ||
2395 | this.legendwidth = 0; | ||
2396 | if(!this.no_legend) { | ||
2397 | if(this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST){ | ||
2398 | if (this.leg_place(1) === -1) return -1; // FIXME | ||
2399 | } | ||
2400 | } | ||
2401 | |||
2402 | if(this.full_size_mode) { | ||
2403 | this.ximg = this.xsize; | ||
2404 | this.yimg = this.ysize; | ||
2405 | Xmain = this.ximg; | ||
2406 | Ymain = this.yimg; | ||
2407 | |||
2408 | Xmain -= Xylabel;// + Xspacing; | ||
2409 | if((this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST) && !(this.no_legend) ) | ||
2410 | Xmain -= this.legendwidth;// + Xspacing; | ||
2411 | if (this.second_axis_scale != 0) Xmain -= Xylabel; | ||
2412 | if (!(this.no_rrdtool_tag)) Xmain -= Xspacing; | ||
2413 | |||
2414 | Xmain -= Xvertical + Xvertical2; | ||
2415 | |||
2416 | if(Xmain < 1) Xmain = 1; | ||
2417 | this.xsize = Xmain; | ||
2418 | |||
2419 | if (!(this.no_legend)) { | ||
2420 | if(this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH){ | ||
2421 | this.legendwidth = this.ximg; | ||
2422 | if (this.leg_place(0) === -1) return -1; | ||
2423 | } | ||
2424 | } | ||
2425 | |||
2426 | if( (this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH) && !(this.no_legend) ) | ||
2427 | Ymain -= Yxlabel + this.legendheight; | ||
2428 | else Ymain -= Yxlabel; | ||
2429 | |||
2430 | Ymain -= Ytitle; | ||
2431 | |||
2432 | if (this.nolegened) Ymain -= 0.5*Yspacing; | ||
2433 | if (this.watermark) Ymain -= Ywatermark; | ||
2434 | if(Ymain < 1) Ymain = 1; | ||
2435 | this.ysize = Ymain; | ||
2436 | } else { | ||
2437 | if (elements) { | ||
2438 | // Xmain = this.xsize; // + Xspacing; | ||
2439 | Xmain = this.xsize + Xspacing; //FIXME ??? | ||
2440 | Ymain = this.ysize; | ||
2441 | } | ||
2442 | this.ximg = Xmain + Xylabel; | ||
2443 | if (!this.no_rrdtool_tag) this.ximg += Xspacing; | ||
2444 | |||
2445 | if( (this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST) && !this.no_legend ) | ||
2446 | this.ximg += this.legendwidth;// + Xspacing; | ||
2447 | if (this.second_axis_scale != 0) this.ximg += Xylabel; | ||
2448 | |||
2449 | this.ximg += Xvertical + Xvertical2; | ||
2450 | |||
2451 | if (!(this.no_legend)) { | ||
2452 | if(this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH){ | ||
2453 | this.legendwidth = this.ximg; | ||
2454 | if (this.leg_place(0) === -1) return -1; | ||
2455 | } | ||
2456 | } | ||
2457 | |||
2458 | this.yimg = Ymain + Yxlabel; | ||
2459 | if( (this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH) && !(this.no_legend) ) | ||
2460 | this.yimg += this.legendheight; | ||
2461 | |||
2462 | if (Ytitle) this.yimg += Ytitle; | ||
2463 | else this.yimg += 1.5 * Yspacing; | ||
2464 | |||
2465 | if (this.no_legend) this.yimg += 0.5*Yspacing; | ||
2466 | if (this.watermark) this.yimg += Ywatermark; | ||
2467 | } | ||
2468 | |||
2469 | |||
2470 | if (!this.no_legend) { | ||
2471 | if(this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST){ | ||
2472 | if (this.leg_place(0) === -1) return -1; | ||
2473 | } | ||
2474 | } | ||
2475 | |||
2476 | switch(this.legendposition){ | ||
2477 | case RrdGraph.LEGEND_POS_NORTH: | ||
2478 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
2479 | this.yOriginTitle = 0; | ||
2480 | this.xOriginLegend = 0; | ||
2481 | this.yOriginLegend = Math.round(Ytitle); | ||
2482 | this.xOriginLegendY = 0; | ||
2483 | this.yOriginLegendY = Math.round(Ytitle + this.legendheight + (Ymain / 2) + Yxlabel); | ||
2484 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
2485 | this.yorigin = Math.round(Ytitle + this.legendheight + Ymain); | ||
2486 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
2487 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
2488 | this.yOriginLegendY2 = Math.round(Ytitle + this.legendheight + (Ymain / 2) + Yxlabel); | ||
2489 | break; | ||
2490 | case RrdGraph.LEGEND_POS_WEST: | ||
2491 | this.xOriginTitle = Math.round(this.legendwidth + this.xsize / 2); | ||
2492 | this.yOriginTitle = 0; | ||
2493 | this.xOriginLegend = 0; | ||
2494 | this.yOriginLegend = Math.round(Ytitle); | ||
2495 | this.xOriginLegendY = Math.round(this.legendwidth); | ||
2496 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
2497 | this.xorigin = Math.round(this.legendwidth + Xvertical + Xylabel); | ||
2498 | this.yorigin = Math.round(Ytitle + Ymain); | ||
2499 | this.xOriginLegendY2 = Math.round(this.legendwidth + Xvertical + Xylabel + Xmain); | ||
2500 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
2501 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
2502 | break; | ||
2503 | case RrdGraph.LEGEND_POS_SOUTH: | ||
2504 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
2505 | this.yOriginTitle = 0; | ||
2506 | this.xOriginLegend = 0; | ||
2507 | this.yOriginLegend = Math.round(Ytitle + Ymain + Yxlabel); | ||
2508 | this.xOriginLegendY = 0; | ||
2509 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
2510 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
2511 | this.yorigin = Math.round(Ytitle + Ymain); | ||
2512 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
2513 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
2514 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
2515 | break; | ||
2516 | case RrdGraph.LEGEND_POS_EAST: | ||
2517 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
2518 | this.yOriginTitle = 0; | ||
2519 | this.xOriginLegend = Math.round(Xvertical + Xylabel + Xmain + Xvertical2); | ||
2520 | if (this.second_axis_scale != 0) this.xOriginLegend += Xylabel; | ||
2521 | this.yOriginLegend = Math.round(Ytitle); | ||
2522 | this.xOriginLegendY = 0; | ||
2523 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
2524 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
2525 | this.yorigin = Math.round(Ytitle + Ymain); | ||
2526 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
2527 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
2528 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
2529 | |||
2530 | if (!this.no_rrdtool_tag){ | ||
2531 | this.xOriginTitle += Xspacing; | ||
2532 | this.xOriginLegend += Xspacing; | ||
2533 | this.xOriginLegendY += Xspacing; | ||
2534 | this.xorigin += Xspacing; | ||
2535 | this.xOriginLegendY2 += Xspacing; | ||
2536 | } | ||
2537 | break; | ||
2538 | } | ||
2539 | this.xtr(0); | ||
2540 | this.ytr(Number.NaN); | ||
2541 | return 0; | ||
2542 | }; | ||
2543 | |||
2544 | RrdGraph.prototype.graph_paint = function() | ||
2545 | { | ||
2546 | if (this.logarithmic && this.minval <= 0) | ||
2547 | throw new RrdGraphError("for a logarithmic yaxis you must specify a lower-limit > 0"); | ||
2548 | |||
2549 | //var start_end = RrdTime.proc_start_end(this.start_t, this.end_t); | ||
2550 | //this.start = start_end[0]; | ||
2551 | //this.end = start_end[1]; | ||
2552 | |||
2553 | if (this.start < 3600 * 24 * 365 * 10) | ||
2554 | throw new RrdGraphError("the first entry to fetch should be after 1980 ("+this.start+")"); | ||
2555 | |||
2556 | if (this.end < this.start) | ||
2557 | throw new RrdGraphError("start ("+this.start+") should be less than end ("+this.end+")"); | ||
2558 | |||
2559 | //this.xlab_form = null | ||
2560 | this.xlab_user = { minsec: -1, length: 0, gridtm: 0, gridst: 0, mgridtm: 0, mgridst: 0, labtm: 0, labst: 0, precis: 0, stst: null }; | ||
2561 | this.ygrid_scale = { gridstep: 0.0, labfact:0 , labfmt: null }; | ||
2562 | this.minval = this.setminval; | ||
2563 | this.maxval = this.setmaxval; | ||
2564 | |||
2565 | this.step = Math.max(this.step, (this.end - this.start) / this.xsize); | ||
2566 | |||
2567 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
2568 | this.gdes[i].step = 0; // FIXME 0? | ||
2569 | this.gdes[i].step_orig = this.step; | ||
2570 | this.gdes[i].start = this.start; // FIXME SHIFT | ||
2571 | // this.gdes[i].start_orig = this.start; | ||
2572 | this.gdes[i].end = this.end; // FIXME SHIFT | ||
2573 | // this.gdes[i].end_orig = this.end; | ||
2574 | } | ||
2575 | |||
2576 | var areazero = 0.0 | ||
2577 | var lastgdes = null; | ||
2578 | |||
2579 | if (this.data_fetch() === -1) | ||
2580 | return -1; | ||
2581 | if (this.data_calc() === -1) | ||
2582 | return -1; | ||
2583 | var i = this.print_calc(); | ||
2584 | if (i < 0) | ||
2585 | return -1; | ||
2586 | if (this.graph_size_location(i) === -1) | ||
2587 | return -1; | ||
2588 | |||
2589 | if (this.data_proc() === -1) | ||
2590 | return -1; | ||
2591 | if (!this.logarithmic) | ||
2592 | this.si_unit(); | ||
2593 | if (!this.rigid && !this.logarithmic) | ||
2594 | this.expand_range(); | ||
2595 | |||
2596 | if (this.magfact === 0) this.magfact =1; // FIXME logarithmic ¿? | ||
2597 | |||
2598 | if (!this.calc_horizontal_grid()) | ||
2599 | return -1; | ||
2600 | |||
2601 | this.ytr(Number.NaN); | ||
2602 | |||
2603 | this.gfx.size(this.ximg, this.yimg); | ||
2604 | |||
2605 | this.gfx.new_area(0, 0, 0, this.yimg, this.ximg, this.yimg, this.GRC.BACK); | ||
2606 | this.gfx.add_point(this.ximg, 0); | ||
2607 | this.gfx.close_path(); | ||
2608 | |||
2609 | this.gfx.new_area(this.xorigin, this.yorigin, this.xorigin + this.xsize, | ||
2610 | this.yorigin, this.xorigin + this.xsize, this.yorigin - this.ysize, this.GRC.CANVAS); | ||
2611 | this.gfx.add_point(this.xorigin, this.yorigin - this.ysize); | ||
2612 | this.gfx.close_path(); | ||
2613 | |||
2614 | //this.ctx.rect(this.xorigin, this.yorigin - this.ysize - 1.0, this.xsize, this.ysize + 2.0); | ||
2615 | //this.ctx.clip(); | ||
2616 | |||
2617 | if (this.minval > 0.0) areazero = this.minval; | ||
2618 | if (this.maxval < 0.0) areazero = this.maxval; | ||
2619 | |||
2620 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
2621 | switch (this.gdes[i].gf) { | ||
2622 | case RrdGraphDesc.GF_CDEF: | ||
2623 | case RrdGraphDesc.GF_VDEF: | ||
2624 | case RrdGraphDesc.GF_DEF: | ||
2625 | case RrdGraphDesc.GF_PRINT: | ||
2626 | case RrdGraphDesc.GF_GPRINT: | ||
2627 | case RrdGraphDesc.GF_COMMENT: | ||
2628 | case RrdGraphDesc.GF_TEXTALIGN: | ||
2629 | case RrdGraphDesc.GF_HRULE: | ||
2630 | case RrdGraphDesc.GF_VRULE: | ||
2631 | case RrdGraphDesc.GF_XPORT: | ||
2632 | case RrdGraphDesc.GF_SHIFT: | ||
2633 | break; | ||
2634 | case RrdGraphDesc.GF_TICK: | ||
2635 | for (var ii = 0; ii < this.xsize; ii++) { | ||
2636 | if (!isNaN(this.gdes[i].p_data[ii]) && this.gdes[i].p_data[ii] != 0.0) { | ||
2637 | if (this.gdes[i].yrule > 0) { | ||
2638 | this.gfx.line(this.xorigin + ii, this.yorigin + 1.0, | ||
2639 | this.xorigin + ii, this.yorigin - this.gdes[i].yrule * this.ysize, 1.0, this.gdes[i].col); | ||
2640 | } else if (this.gdes[i].yrule < 0) { | ||
2641 | this.gfx.line(this.xorigin + ii, this.yorigin - this.ysize - 1.0, | ||
2642 | this.xorigin + ii, this.yorigin - this.ysize - this.gdes[i].yrule * this.ysize, 1.0, this.gdes[i].col); | ||
2643 | } | ||
2644 | } | ||
2645 | } | ||
2646 | break; | ||
2647 | case RrdGraphDesc.GF_LINE: | ||
2648 | case RrdGraphDesc.GF_AREA: | ||
2649 | var diffval = this.maxval - this.minval; | ||
2650 | var maxlimit = this.maxval + 9 * diffval; | ||
2651 | var minlimit = this.minval - 9 * diffval; | ||
2652 | for (var ii = 0; ii < this.xsize; ii++) { | ||
2653 | if (!isNaN(this.gdes[i].p_data[ii])) { // FIXME NaN < ??? | ||
2654 | if (!isFinite(this.gdes[i].p_data[ii])) { | ||
2655 | if (this.gdes[i].p_data[ii] > 0) this.gdes[i].p_data[ii] = this.maxval; | ||
2656 | else this.gdes[i].p_data[ii] = this.minval; | ||
2657 | } | ||
2658 | if (this.gdes[i].p_data[ii] > maxlimit) this.gdes[i].p_data[ii] = maxlimit; | ||
2659 | if (this.gdes[i].p_data[ii] < minlimit) this.gdes[i].p_data[ii] = minlimit; | ||
2660 | } | ||
2661 | } | ||
2662 | var color = this.parse_color(this.gdes[i].col); // if (this.gdes[i].col.alpha != 0.0) | ||
2663 | if (color[3] != 0.0) { | ||
2664 | if (this.gdes[i].gf === RrdGraphDesc.GF_LINE) { | ||
2665 | var last_y = 0.0; | ||
2666 | var draw_on = false; | ||
2667 | |||
2668 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
2669 | this.gfx.stroke_begin(this.gdes[i].linewidth, this.gdes[i].col); | ||
2670 | for (var ii = 1; ii < this.xsize; ii++) { | ||
2671 | if (isNaN(this.gdes[i].p_data[ii]) || (this.slopemode && isNaN(this.gdes[i].p_data[ii - 1]))) { | ||
2672 | draw_on = false; | ||
2673 | continue; | ||
2674 | } | ||
2675 | if (!draw_on) { | ||
2676 | last_y = this.ytr(this.gdes[i].p_data[ii]); | ||
2677 | if (!this.slopemode) { | ||
2678 | var x = ii - 1 + this.xorigin; | ||
2679 | var y = last_y; | ||
2680 | this.gfx.moveTo(x, y); | ||
2681 | x = ii + this.xorigin; | ||
2682 | y = last_y; | ||
2683 | this.gfx.lineTo(x, y) | ||
2684 | } else { | ||
2685 | var x = ii - 1 + this.xorigin; | ||
2686 | var y = this.ytr(this.gdes[i].p_data[ii - 1]); | ||
2687 | this.gfx.moveTo(x, y); | ||
2688 | x = ii + this.xorigin; | ||
2689 | y = last_y; | ||
2690 | this.gfx.lineTo(x, y); | ||
2691 | } | ||
2692 | draw_on = true; | ||
2693 | } else { | ||
2694 | var x1 = ii + this.xorigin; | ||
2695 | var y1 = this.ytr(this.gdes[i].p_data[ii]); | ||
2696 | |||
2697 | if (!this.slopemode && !this.AlmostEqual2sComplement(y1, last_y, 4)) { | ||
2698 | var x = ii - 1 + this.xorigin; | ||
2699 | var y = y1; | ||
2700 | |||
2701 | this.gfx.lineTo(x, y); | ||
2702 | } | ||
2703 | last_y = y1; | ||
2704 | this.gfx.lineTo(x1, y1); | ||
2705 | } | ||
2706 | } | ||
2707 | this.gfx.stroke_end(); | ||
2708 | } else { | ||
2709 | var idxI = -1; | ||
2710 | var foreY = []; | ||
2711 | var foreX = []; | ||
2712 | var backY = []; | ||
2713 | var backX = []; | ||
2714 | var drawem = false; | ||
2715 | |||
2716 | for (ii = 0; ii <= this.xsize; ii++) { | ||
2717 | var ybase, ytop; | ||
2718 | |||
2719 | if (idxI > 0 && (drawem || ii === this.xsize)) { | ||
2720 | var cntI = 1; | ||
2721 | var lastI = 0; | ||
2722 | |||
2723 | while (cntI < idxI && this.AlmostEqual2sComplement(foreY [lastI], foreY[cntI], 4) && | ||
2724 | this.AlmostEqual2sComplement(foreY [lastI], foreY [cntI + 1], 4)) cntI++; | ||
2725 | this.gfx.new_area(backX[0], backY[0], foreX[0], foreY[0], foreX[cntI], foreY[cntI], this.gdes[i].col); | ||
2726 | while (cntI < idxI) { | ||
2727 | lastI = cntI; | ||
2728 | cntI++; | ||
2729 | while (cntI < idxI && | ||
2730 | this.AlmostEqual2sComplement(foreY [lastI], foreY[cntI], 4) && | ||
2731 | this.AlmostEqual2sComplement(foreY [lastI], foreY [cntI + 1], 4)) cntI++; | ||
2732 | this.gfx.add_point(foreX[cntI], foreY[cntI]); | ||
2733 | } | ||
2734 | this.gfx.add_point(backX[idxI], backY[idxI]); | ||
2735 | while (idxI > 1) { | ||
2736 | lastI = idxI; | ||
2737 | idxI--; | ||
2738 | while (idxI > 1 && | ||
2739 | this.AlmostEqual2sComplement(backY [lastI], backY[idxI], 4) && | ||
2740 | this.AlmostEqual2sComplement(backY [lastI], backY [idxI - 1], 4)) idxI--; | ||
2741 | this.gfx.add_point(backX[idxI], backY[idxI]); | ||
2742 | } | ||
2743 | idxI = -1; | ||
2744 | drawem = false; | ||
2745 | this.gfx.close_path(); | ||
2746 | } | ||
2747 | if (drawem) { | ||
2748 | drawem = false; | ||
2749 | idxI = -1; | ||
2750 | } | ||
2751 | if (ii === this.xsize) | ||
2752 | break; | ||
2753 | if (!this.slopemode && ii === 0) | ||
2754 | continue; | ||
2755 | if (isNaN(this.gdes[i].p_data[ii])) { | ||
2756 | drawem = true; | ||
2757 | continue; | ||
2758 | } | ||
2759 | ytop = this.ytr(this.gdes[i].p_data[ii]); | ||
2760 | if (lastgdes && this.gdes[i].stack) ybase = this.ytr(lastgdes.p_data[ii]); | ||
2761 | else ybase = this.ytr(areazero); | ||
2762 | if (ybase === ytop) { | ||
2763 | drawem = true; | ||
2764 | continue; | ||
2765 | } | ||
2766 | if (ybase > ytop) { | ||
2767 | var extra = ytop; | ||
2768 | ytop = ybase; | ||
2769 | ybase = extra; | ||
2770 | } | ||
2771 | if (!this.slopemode) { | ||
2772 | backY[++idxI] = ybase - 0.2; | ||
2773 | backX[idxI] = ii + this.xorigin - 1; | ||
2774 | foreY[idxI] = ytop + 0.2; | ||
2775 | foreX[idxI] = ii + this.xorigin - 1; | ||
2776 | } | ||
2777 | backY[++idxI] = ybase - 0.2; | ||
2778 | backX[idxI] = ii + this.xorigin; | ||
2779 | foreY[idxI] = ytop + 0.2; | ||
2780 | foreX[idxI] = ii + this.xorigin; | ||
2781 | } | ||
2782 | } | ||
2783 | } | ||
2784 | /* if color != 0x0 */ | ||
2785 | /* make sure we do not run into trouble when stacking on NaN */ | ||
2786 | for (ii = 0; ii < this.xsize; ii++) { | ||
2787 | if (isNaN(this.gdes[i].p_data[ii])) { | ||
2788 | if (lastgdes && (this.gdes[i].stack)) this.gdes[i].p_data[ii] = lastgdes.p_data[ii]; | ||
2789 | else this.gdes[i].p_data[ii] = areazero; | ||
2790 | } | ||
2791 | } | ||
2792 | lastgdes = this.gdes[i]; //lastgdes = &(this.gdes[i]); | ||
2793 | break; | ||
2794 | case RrdGraphDesc.GF_STACK: | ||
2795 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
2796 | break; | ||
2797 | } | ||
2798 | } | ||
2799 | //cairo_reset_clip(this.cr); | ||
2800 | if (!this.only_graph) | ||
2801 | this.grid_paint(); | ||
2802 | if (!this.only_graph) | ||
2803 | this.axis_paint(); | ||
2804 | /* the RULES are the last thing to paint ... */ | ||
2805 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
2806 | switch (this.gdes[i].gf) { | ||
2807 | case RrdGraphDesc.GF_HRULE: | ||
2808 | if (this.gdes[i].yrule >= this.minval && this.gdes[i].yrule <= this.maxval) { | ||
2809 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
2810 | this.gfx.line(this.xorigin, this.ytr(this.gdes[i].yrule), | ||
2811 | this.xorigin + this.xsize, this.ytr(this.gdes[i].yrule), 1.0, this.gdes[i].col); | ||
2812 | } | ||
2813 | break; | ||
2814 | case RrdGraphDesc.GF_VRULE: | ||
2815 | if (this.gdes[i].xrule >= this.start && this.gdes[i].xrule <= this.end) { | ||
2816 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
2817 | this.gfx.line(this.xtr(this.gdes[i].xrule), this.yorigin, | ||
2818 | this.xtr(this.gdes[i].xrule), this.yorigin - this.ysize, 1.0, this.gdes[i].col); | ||
2819 | } | ||
2820 | break; | ||
2821 | default: | ||
2822 | break; | ||
2823 | } | ||
2824 | } | ||
2825 | return 0; | ||
2826 | }; | ||
2827 | |||
2828 | RrdGraph.prototype.find_var = function(key) | ||
2829 | { | ||
2830 | for (var ii = 0, gdes_c = this.gdes.length; ii < gdes_c; ii++) { | ||
2831 | if ((this.gdes[ii].gf === RrdGraphDesc.GF_DEF || | ||
2832 | this.gdes[ii].gf === RrdGraphDesc.GF_VDEF || | ||
2833 | this.gdes[ii].gf === RrdGraphDesc.GF_CDEF) | ||
2834 | && this.gdes[ii].vname === key) { | ||
2835 | return ii; | ||
2836 | } | ||
2837 | } | ||
2838 | return -1; | ||
2839 | }; | ||
2840 | |||
2841 | RrdGraph.prototype.gdes_add_def = function (vname, rrdfile, name, cf, step, start, end, reduce) | ||
2842 | { | ||
2843 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_DEF, vname, rrdfile, name, cf, step, start, end, reduce)); | ||
2844 | }; | ||
2845 | |||
2846 | RrdGraph.prototype.gdes_add_cdef = function (vname, rpn) | ||
2847 | { | ||
2848 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_CDEF, vname, rpn)); | ||
2849 | }; | ||
2850 | |||
2851 | RrdGraph.prototype.gdes_add_vdef = function (vname, rpn) | ||
2852 | { | ||
2853 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VDEF, vname, rpn)); | ||
2854 | }; | ||
2855 | |||
2856 | RrdGraph.prototype.gdes_add_shift = function (vname, offset) | ||
2857 | { | ||
2858 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_SHIFT, vname, offset)); | ||
2859 | }; | ||
2860 | |||
2861 | RrdGraph.prototype.gdes_add_line = function (width, value, color, legend, stack) | ||
2862 | { | ||
2863 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_LINE, width, value, color, legend, stack)); | ||
2864 | }; | ||
2865 | |||
2866 | RrdGraph.prototype.gdes_add_area = function (value, color, legend, stack) | ||
2867 | { | ||
2868 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_AREA, value, color, legend, stack)); | ||
2869 | }; | ||
2870 | |||
2871 | RrdGraph.prototype.gdes_add_tick = function (vname, color, fraction, legend) | ||
2872 | { | ||
2873 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_TICK, vname, color, fraction, legend)); | ||
2874 | }; | ||
2875 | |||
2876 | RrdGraph.prototype.gdes_add_gprint = function (vname, cf, format, strftimefmt) | ||
2877 | { | ||
2878 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_GPRINT, vname, cf, format, strftimefmt)); | ||
2879 | }; | ||
2880 | |||
2881 | RrdGraph.prototype.gdes_add_comment = function (text) | ||
2882 | { | ||
2883 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_COMMENT, text)); | ||
2884 | }; | ||
2885 | |||
2886 | RrdGraph.prototype.gdes_add_textalign = function (align) | ||
2887 | { | ||
2888 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_TEXTALING, align)); | ||
2889 | }; | ||
2890 | |||
2891 | RrdGraph.prototype.gdes_add_vrule = function (time, color, legend) | ||
2892 | { | ||
2893 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VRULE, time, color, legend)); | ||
2894 | }; | ||
2895 | |||
2896 | RrdGraph.prototype.gdes_add_hrule = function (value, color, legend) | ||
2897 | { | ||
2898 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_HRULE, value, color, legend)); | ||
2899 | }; | ||
2900 | |||
2901 | RrdGraph.prototype.tmt_conv = function (str) | ||
2902 | { | ||
2903 | switch (str) { | ||
2904 | case 'SECOND': return RrdGraph.TMT_SECOND; | ||
2905 | case 'MINUTE': return RrdGraph.TMT_MINUTE; | ||
2906 | case 'HOUR': return RrdGraph.TMT_HOUR; | ||
2907 | case 'DAY': return RrdGraph.TMT_DAY; | ||
2908 | case 'WEEK': return RrdGraph.TMT_WEEK; | ||
2909 | case 'MONTH': return RrdGraph.TMT_MONTH; | ||
2910 | case 'YEAR': return RrdGraph.TMT_YEAR; | ||
2911 | } | ||
2912 | return -1; | ||
2913 | }; | ||
2914 | |||
diff --git a/js/RrdJson.js b/js/RrdJson.js new file mode 100644 index 0000000..85b7f11 --- /dev/null +++ b/js/RrdJson.js | |||
@@ -0,0 +1,588 @@ | |||
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 <manuel.luis at gmail.com> | ||
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 name '"+name+"'"; | ||
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 | break; | ||
143 | case 'start': | ||
144 | this.graph.start_t = new RrdTime(this.json.start); | ||
145 | break; | ||
146 | case 'tabwidth': | ||
147 | this.graph.tabwidth = this.json.tabwidth; | ||
148 | break; | ||
149 | case 'title': | ||
150 | this.graph.title = this.json.title; | ||
151 | break; | ||
152 | case 'upper_limit': | ||
153 | if (this.json.upper_limit === null) this.graph.setmaxval = Number.NaN; | ||
154 | else this.graph.setmaxval = this.json.upper_limit; | ||
155 | break; | ||
156 | case 'vertical_label': | ||
157 | this.graph.ylegend = this.json.vertical_label; | ||
158 | break; | ||
159 | case 'watermark': | ||
160 | this.graph.watermark = this.json.watermark; | ||
161 | break; | ||
162 | case 'width': | ||
163 | this.graph.xsize = this.json.width; | ||
164 | if (this.graph.xsize < 10) | ||
165 | throw "width below 10 pixels"; | ||
166 | break; | ||
167 | case 'units_exponent': | ||
168 | this.graph.unitsexponent = this.json.units_exponent; | ||
169 | break; | ||
170 | case 'x_grid': | ||
171 | break; | ||
172 | case 'alt_ygrid': | ||
173 | this.graph.alt_ygrid = this.json.alt_ygrid; | ||
174 | break; | ||
175 | case 'y_grid': | ||
176 | break; | ||
177 | case 'lazy': | ||
178 | this.graph.lazy = this.json.lazy; | ||
179 | break; | ||
180 | case 'units': | ||
181 | break; | ||
182 | case 'disable_rrdtool_tag': | ||
183 | this.graph.no_rrdtool_tag = this.json.disable_rrdtool_tag; | ||
184 | break; | ||
185 | case 'right_axis': | ||
186 | break; | ||
187 | case 'right_axis_label': | ||
188 | this.graph.second_axis_legend = this.json.right_axis_label; | ||
189 | break; | ||
190 | case 'right_axis_format': | ||
191 | this.graph.second_axis_format = this.json.right_axis_format; | ||
192 | break; | ||
193 | case 'legend_position': | ||
194 | if (this.json.legend_position === "north") { | ||
195 | this.graph.legendposition = this.graph.LEGEND_POS.NORTH; | ||
196 | } else if (this.json.legend_position === "west") { | ||
197 | this.graph.legendposition = this.graph.LEGEND_POS.WEST; | ||
198 | } else if (this.json.legend_position === "south") { | ||
199 | this.graph.legendposition = this.graph.LEGEND_POS.SOUTH; | ||
200 | } else if (this.json.legend_position === "east") { | ||
201 | this.graph.legendposition = this.graph.LEGEND_POS.EAST; | ||
202 | } else { | ||
203 | throw "unknown legend-position '"+value+"'"; | ||
204 | } | ||
205 | break; | ||
206 | case 'legend_direction': | ||
207 | if (this.json.legend_direction === "topdown") { | ||
208 | this.graph.legenddirection = this.graph.LEGEND_DIR.TOP_DOWN; | ||
209 | } else if (this.json.legend_direction === "bottomup") { | ||
210 | this.graph.legenddirection = this.graph.LEGEND_DIR.BOTTOM_UP; | ||
211 | } else { | ||
212 | throw "unknown legend-position '"+value+"'"; | ||
213 | } | ||
214 | break; | ||
215 | case 'border': | ||
216 | this.graph.draw_3d_border = this.json.border; | ||
217 | break; | ||
218 | case 'grid_dash': | ||
219 | if (this.json.grid_dash.length !== 2) | ||
220 | throw "expected grid-dash format float:float"; | ||
221 | this.graph.grid_dash_on = this.json.grid_dash[0]; | ||
222 | this.graph.grid_dash_off = this.json.grid_dash[1]; | ||
223 | break; | ||
224 | case 'dynamic_labels': | ||
225 | this.graph.dynamic_labels = this.json.dynamic_labels; | ||
226 | break; | ||
227 | case 'gdes': | ||
228 | this.parse_gdes(this.json.gdes); | ||
229 | break; | ||
230 | default: | ||
231 | throw 'Unknow option "'+option+'"'; | ||
232 | } | ||
233 | } | ||
234 | var start_end = RrdTime.proc_start_end(this.graph.start_t, this.graph.end_t); // FIXME here? | ||
235 | this.graph.start = start_end[0]; | ||
236 | this.graph.end = start_end[1]; | ||
237 | }, | ||
238 | parse_gdes: function (gdes) | ||
239 | { | ||
240 | for (var i = 0, gdes_c = gdes.length; i < gdes_c; i++) { | ||
241 | switch (gdes[i].type) { | ||
242 | // GPRINT:vname:format | ||
243 | case 'GPRINT': | ||
244 | this.graph.gdes_add_gprint(gdes[i].vname, gdes[i].cf, gdes[i].format, gdes[i].strftm); | ||
245 | break; | ||
246 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
247 | case 'LINE': | ||
248 | this.graph.gdes_add_line(gdes[i].width, gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack); | ||
249 | break; | ||
250 | // AREA:value[#color][:[legend][:STACK]] | ||
251 | case 'AREA': | ||
252 | this.graph.gdes_add_area(gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack); | ||
253 | break; | ||
254 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
255 | case 'TICK': | ||
256 | this.graph.gdes_add_tick(gdes[i].vname, gdes[i].color, gdes[i].fraction, gdes[i].legend); | ||
257 | break; | ||
258 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
259 | case 'HRULE': | ||
260 | this.graph.gdes_add_hrule(gdes[i].value, gdes[i].color, gdes[i].legend); | ||
261 | break; | ||
262 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
263 | case 'VRULE': | ||
264 | this.graph.gdes_add_vrule(gdes[i].time, gdes[i].color, gdes[i].legend); | ||
265 | break; | ||
266 | // COMMENT:text | ||
267 | case 'COMMENT': | ||
268 | this.graph.gdes_add_comment(gdes[i].legend); | ||
269 | break; | ||
270 | // TEXTALIGN:{left|right|justified|center} | ||
271 | case 'TEXTALIGN': | ||
272 | switch (gdes[i].align) { | ||
273 | case 'left': | ||
274 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_LEFT); | ||
275 | break | ||
276 | case 'right': | ||
277 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_RIGHT); | ||
278 | break | ||
279 | case 'justified': | ||
280 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_JUSTIFIED); | ||
281 | break | ||
282 | case 'center': | ||
283 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_CENTER); | ||
284 | break | ||
285 | } | ||
286 | break; | ||
287 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
288 | case 'DEF': | ||
289 | this.graph.gdes_add_def(gdes[i].vname, gdes[i].rrdfile, gdes[i].name, gdes[i].cf, gdes[i].step, gdes[i].start, gdes[i].end, gdes[i].reduce) | ||
290 | break; | ||
291 | // CDEF:vname=RPN expression | ||
292 | case 'CDEF': | ||
293 | this.graph.gdes_add_cdef(gdes[i].vname, gdes[i].rpn); | ||
294 | break; | ||
295 | // VDEF:vname=RPN expression | ||
296 | case 'VDEF': | ||
297 | this.graph.gdes_add_vdef(gdes[i].vname, gdes[i].rpn); | ||
298 | break; | ||
299 | // SHIFT:vname:offset | ||
300 | case 'SHIFT': | ||
301 | this.graph.gdes_add_shift(gdes[i].vname, gdes[i].offset); | ||
302 | break; | ||
303 | } | ||
304 | } | ||
305 | }, | ||
306 | dump: function(full) | ||
307 | { | ||
308 | this.json = {}; | ||
309 | |||
310 | if (full === undefined) full = false; | ||
311 | |||
312 | if (this.graph.alt_autoscale != false || full) | ||
313 | this.json.alt_autoscale = this.graph.alt_autoscale; | ||
314 | |||
315 | if (this.graph.base != 1000 || full) | ||
316 | this.json.base = this.graph.base; | ||
317 | |||
318 | this.json.color = {}; | ||
319 | |||
320 | if (this.graph.GRC.CANVAS != 'rgba(255, 255, 255, 1.0)' || full) | ||
321 | this.json.color.CANVAS = this.graph.GRC.CANVAS; | ||
322 | if (this.graph.GRC.BACK != 'rgba(242,242, 242, 1.0)' || full) | ||
323 | this.json.color.BACK = this.graph.GRC.BACK; | ||
324 | if (this.graph.GRC.SHADEA != 'rgba(207, 207, 207, 1.0)' || full) | ||
325 | this.json.color.SHADEA = this.graph.GRC.SHADEA; | ||
326 | if (this.graph.GRC.SHADEB != 'rgba(158, 158, 158, 1.0)' || full) | ||
327 | this.json.color.SHADEB = this.graph.GRC.SHADEB; | ||
328 | if (this.graph.GRC.GRID != 'rgba(143, 143, 143, 0.75)' || full) | ||
329 | this.json.color.GRID = this.graph.GRC.GRID; | ||
330 | if (this.graph.GRC.MGRID != 'rgba(222, 79, 79, 0.60)' || full) | ||
331 | this.json.color.MGRID = this.graph.GRC.MGRID; | ||
332 | if (this.graph.GRC.FONT != 'rgba(0, 0, 0, 1.0)' || full) | ||
333 | this.json.color.FONT = this.graph.GRC.FONT; | ||
334 | if (this.graph.GRC.ARROW != 'rgba(127, 31, 31, 1.0)' || full) | ||
335 | this.json.color.ARROW = this.graph.GRC.ARROW; | ||
336 | if (this.graph.GRC.AXIS != 'rgba(31, 31, 31, 1.0)' || full) | ||
337 | this.json.color.AXIS = this.graph.GRC.AXIS; | ||
338 | if (this.graph.GRC.FRAME != 'rgba(0, 0, 0, 1.0)' || full) | ||
339 | this.json.color.FRAME = this.graph.GRC.FRAME; | ||
340 | |||
341 | if (Object.keys(this.json.color) == 0) delete this.json.color; | ||
342 | |||
343 | if (this.graph.full_size_mode != false || full) | ||
344 | this.json.full_size_mode = this.graph.full_size_mode; | ||
345 | |||
346 | if (this.graph.slopemode != false || full) | ||
347 | this.json.slope_mode = this.graph.slopemode; | ||
348 | |||
349 | this.json.end = this.graph.end_t.tspec; | ||
350 | this.json.start = this.graph.start_t.tspec; | ||
351 | |||
352 | if (this.graph.force_rules_legend != false || full) | ||
353 | this.json.force_rules_legend = this.graph.force_rules_legend; | ||
354 | |||
355 | if (this.graph.no_legend != false || full) | ||
356 | this.json.no_legend = this.graph.no_legend; | ||
357 | |||
358 | this.json.width = this.graph.xsize; | ||
359 | this.json.height = this.graph.ysize; | ||
360 | |||
361 | if (this.graph.no_minor != false || full) | ||
362 | this.json.no_minor = this.graph.no_minor; | ||
363 | |||
364 | if (this.graph.alt_autoscale_min != false || full) | ||
365 | this.json.alt_autoscale_min = this.graph.alt_autoscale_min; | ||
366 | |||
367 | if (this.graph.only_graph != false || full) | ||
368 | this.json.only_graph = this.graph.only_graph; | ||
369 | |||
370 | if (this.graph.unitslength != 6 || full) | ||
371 | this.json.units_length = this.graph.unitslength; | ||
372 | |||
373 | if (!isNaN(this.graph.setminval) || full) | ||
374 | this.json.lower_limit = this.graph.setminval; | ||
375 | |||
376 | if (this.graph.alt_autoscale_max != false || full) | ||
377 | this.json.alt_autoscale_max = this.graph.alt_autoscale_max; | ||
378 | |||
379 | if (this.graph.zoom != 1 || full) | ||
380 | this.json.zoom = this.graph.zoom; | ||
381 | |||
382 | if (this.graph.gridfit != true || full) | ||
383 | this.json.no_gridfit = this.graph.gridfit; | ||
384 | |||
385 | this.json.font = {}; | ||
386 | if (this.graph.TEXT.DEFAULT.size != 11 || this.graph.TEXT.LEGEND.font != this.graph.DEFAULT_FONT || full) | ||
387 | this.json.font.DEFAULT = { size: this.graph.TEXT.DEFAULT.size, font: this.graph.TEXT.DEFAULT.font}; | ||
388 | if (this.graph.TEXT.TITLE.size != 12 || this.graph.TEXT.TITLE.font != this.graph.DEFAULT_FONT || full) | ||
389 | this.json.font.TITLE = { size: this.graph.TEXT.TITLE.size, font: this.graph.TEXT.TITLE.font}; | ||
390 | if (this.graph.TEXT.AXIS.size != 10 || this.graph.TEXT.AXIS.font != this.graph.DEFAULT_FONT || full) | ||
391 | this.json.font.AXIS = { size: this.graph.TEXT.AXIS.size, font: this.graph.TEXT.AXIS.font}; | ||
392 | if (this.graph.TEXT.UNIT.size != 11 || this.graph.TEXT.UNIT.font != this.graph.DEFAULT_FONT || full) | ||
393 | this.json.font.UNIT = { size: this.graph.TEXT.UNIT.size, font: this.graph.TEXT.UNIT.font}; | ||
394 | if (this.graph.TEXT.LEGEND.size != 11 || this.graph.TEXT.LEGEND.font != this.graph.DEFAULT_FONT || full) | ||
395 | this.json.font.LEGEND = { size: this.graph.TEXT.LEGEND.size, font: this.graph.TEXT.LEGEND.font}; | ||
396 | if (this.graph.TEXT.WATERMARK.size != 8 || this.graph.TEXT.WATERMARK.font != this.graph.DEFAULT_FONT || full) | ||
397 | this.json.font.WATERMARK = { size: this.graph.TEXT.WATERMARK.size, font: this.graph.TEXT.WATERMARK.font}; | ||
398 | |||
399 | if (Object.keys(this.json.font) == 0) delete this.json.font; | ||
400 | |||
401 | if (this.graph.logarithmic != false || full) | ||
402 | this.json.logarithmic = this.graph.logarithmic; | ||
403 | |||
404 | if (this.graph.rigid != false || full) | ||
405 | this.json.rigid = this.graph.rigid; | ||
406 | |||
407 | // this.json.step = this.graph.step; // FIXME | ||
408 | |||
409 | if (this.graph.tabwidth != 40 || full) | ||
410 | this.json.tabwidth = this.graph.tabwidth; | ||
411 | |||
412 | if (this.graph.title != '' || full) | ||
413 | this.json.title = this.graph.title; | ||
414 | |||
415 | if (!isNaN(this.graph.setmaxval) || full) | ||
416 | this.json.upper_limit = this.graph.setmaxval; | ||
417 | |||
418 | if (this.graph.ylegend != null || full) | ||
419 | this.json.vertical_label = this.graph.ylegend; | ||
420 | |||
421 | if (this.graph.watermark != null || full) | ||
422 | this.json.watermark = this.graph.watermark; | ||
423 | |||
424 | if (this.graph.unitsexponent != 9999 || full) | ||
425 | this.json.units_exponent = this.graph.unitsexponent; | ||
426 | |||
427 | // this.json.x-grid = // FIXME | ||
428 | |||
429 | if (this.graph.alt_ygrid != false || full) | ||
430 | this.json.alt_ygrid = this.graph.alt_ygrid; | ||
431 | |||
432 | // this.json.y_grid = // FIXME | ||
433 | |||
434 | // this.json.lazy = this.graph.lazy; | ||
435 | |||
436 | if (this.graph.force_units_si != false || full) | ||
437 | this.json.units = 'si'; // FIXME | ||
438 | |||
439 | if (this.graph.no_rrdtool_tag != false || full) | ||
440 | this.json.disable_rrdtool_tag = this.graph.no_rrdtool_tag; | ||
441 | |||
442 | // this.json.right_axis = FIXME | ||
443 | |||
444 | if (this.graph.second_axis_legend != null || full) | ||
445 | this.json.right_axis_label = this.graph.second_axis_legend; | ||
446 | if (this.graph.second_axis_format != null || full) | ||
447 | this.json.right_axis_format = this.graph.second_axis_format; | ||
448 | |||
449 | // this.json.legendposition = this.graph.legendposition; // FIXME | ||
450 | // this.json.legend-direction = this.graph.legenddirection; // FIXME | ||
451 | |||
452 | if (this.graph.draw_3d_border != 2 || full) | ||
453 | this.json.border = this.graph.draw_3d_border; | ||
454 | |||
455 | if (this.graph.grid_dash_on != 1 || this.graph.grid_dash_off != 1 || full) | ||
456 | this.json.grid_dash = [this.graph.grid_dash_on, this.graph.grid_dash_off] | ||
457 | |||
458 | if (this.graph.dynamic_labels != false || full) | ||
459 | this.json.dynamic_labels = this.graph.dynamic_labels; | ||
460 | |||
461 | this.json.gdes = []; | ||
462 | for (var i = 0, gdes_c = this.graph.gdes.length; i < gdes_c; i++) { | ||
463 | switch (this.graph.gdes[i].gf) { | ||
464 | // GPRINT:vname:format | ||
465 | case RrdGraphDesc.GF_GPRINT: | ||
466 | this.json.gdes.push({ | ||
467 | type: 'GPRINT', | ||
468 | vname: this.graph.gdes[i].vname, | ||
469 | cf: RrdGraphDesc.cf2str(this.graph.gdes[i].cf), | ||
470 | format: this.graph.gdes[i].format, | ||
471 | strftm: (this.graph.gdes[i].strftm === false ? undefined : this.graph.gdes[i].strftm) }); | ||
472 | break; | ||
473 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
474 | case RrdGraphDesc.GF_LINE: | ||
475 | this.json.gdes.push({ | ||
476 | type: 'LINE', | ||
477 | width: this.graph.gdes[i].linewidth, | ||
478 | value: this.graph.gdes[i].vname, | ||
479 | color: this.graph.gdes[i].col, | ||
480 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)), | ||
481 | stack: (this.graph.gdes[i].stack === false ? undefined : this.graph.gdes[i].stack) }); | ||
482 | break; | ||
483 | // AREA:value[#color][:[legend][:STACK]] | ||
484 | case RrdGraphDesc.GF_AREA: | ||
485 | this.json.gdes.push({ | ||
486 | type: 'AREA', | ||
487 | value: this.graph.gdes[i].vname, | ||
488 | color: this.graph.gdes[i].col, | ||
489 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)), | ||
490 | stack: (this.graph.gdes[i].stack === false ? undefined : this.graph.gdes[i].stack) }); | ||
491 | break; | ||
492 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
493 | case RrdGraphDesc.GF_TICK: | ||
494 | this.json.gdes.push({ | ||
495 | type: 'TICK', | ||
496 | vname: this.graph.gdes[i].vname, | ||
497 | color: this.graph.gdes[i].col, | ||
498 | fraction: this.graph.gdes[i].yrule, | ||
499 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
500 | break; | ||
501 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
502 | case RrdGraphDesc.GF_HRULE: | ||
503 | this.json.gdes.push({ | ||
504 | type: 'HRULE', | ||
505 | value: this.graph.gdes[i].yrule, | ||
506 | color: this.graph.gdes[i].col, | ||
507 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
508 | break; | ||
509 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
510 | case RrdGraphDesc.GF_VRULE: | ||
511 | this.json.gdes.push({ | ||
512 | type: 'VRULE', | ||
513 | time: this.graph.gdes[i].xrule, | ||
514 | color: this.graph.gdes[i].col, | ||
515 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
516 | break; | ||
517 | // COMMENT:text | ||
518 | case RrdGraphDesc.GF_COMMENT: | ||
519 | this.json.gdes.push({ | ||
520 | type: 'COMMENT', | ||
521 | legend: this.graph.gdes[i].legend}); | ||
522 | break; | ||
523 | // TEXTALIGN:{left|right|justified|center} | ||
524 | case RrdGraphDesc.GF_TEXTALIGN: | ||
525 | var align = ''; | ||
526 | switch (this.graph.gdes[i].txtalign) { | ||
527 | case RrdGraphDesc.TXA_LEFT: | ||
528 | align = 'left'; | ||
529 | break | ||
530 | case RrdGraphDesc.TXA_RIGHT: | ||
531 | align = 'right'; | ||
532 | break | ||
533 | case RrdGraphDesc.TXA_JUSTIFIED: | ||
534 | align = 'justified'; | ||
535 | break | ||
536 | case RrdGraphDesc.TXA_CENTER: | ||
537 | align = 'center'; | ||
538 | break | ||
539 | } | ||
540 | |||
541 | this.json.gdes.push({ | ||
542 | type: 'TEXTALIGN', | ||
543 | align: align }); | ||
544 | break; | ||
545 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
546 | case RrdGraphDesc.GF_DEF: | ||
547 | this.json.gdes.push({ | ||
548 | type: 'DEF', | ||
549 | vname: this.graph.gdes[i].vname, | ||
550 | rrdfile: this.graph.gdes[i].rrd, | ||
551 | name: this.graph.gdes[i].ds_nam, | ||
552 | cf: RrdGraphDesc.cf2str(this.graph.gdes[i].cf), | ||
553 | // step: this.graph.gdes[i].step, | ||
554 | step: undefined, | ||
555 | start: undefined, | ||
556 | // start: this.graph.gdes[i].start, // FIXME | ||
557 | end: undefined, | ||
558 | // end: this.graph.gdes[i].end, // FIXME | ||
559 | // reduce: RrdGraphDesc.cf2str(this.graph.gdes[i].cf_reduce) | ||
560 | reduce: undefined | ||
561 | }); | ||
562 | |||
563 | break; | ||
564 | // CDEF:vname=RPN expression | ||
565 | case RrdGraphDesc.GF_CDEF: | ||
566 | this.json.gdes.push({ | ||
567 | type: 'CDEF', | ||
568 | vname: this.graph.gdes[i].vname, | ||
569 | rpn: this.graph.gdes[i].rpnp.rpnexpr}); | ||
570 | break; | ||
571 | // VDEF:vname=RPN expression | ||
572 | case RrdGraphDesc.GF_VDEF: | ||
573 | this.json.gdes.push({ | ||
574 | type: 'VDEF', | ||
575 | vname: this.graph.gdes[i].vname, | ||
576 | rpn: this.graph.gdes[this.graph.gdes[i].vidx].vname+','+this.graph.gdes[i].vf.expr}); | ||
577 | break; | ||
578 | // SHIFT:vname:offset | ||
579 | case RrdGraphDesc.GF_SHIFT: | ||
580 | this.json.gdes.push({ | ||
581 | type: 'VDEF', | ||
582 | vname: this.graph.gdes[i].vname, | ||
583 | offset: this.shidx }); | ||
584 | break; | ||
585 | } | ||
586 | } | ||
587 | } | ||
588 | }; | ||
diff --git a/js/RrdRpn.js b/js/RrdRpn.js new file mode 100644 index 0000000..cbb5e8f --- /dev/null +++ b/js/RrdRpn.js | |||
@@ -0,0 +1,616 @@ | |||
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 <manuel.luis at gmail.com> | ||
20 | **/ | ||
21 | |||
22 | "use strict"; | ||
23 | |||
24 | /** | ||
25 | * RrdRpnError | ||
26 | * @constructor | ||
27 | */ | ||
28 | var RrdRpnError = function (message) | ||
29 | { | ||
30 | this.prototype = Error.prototype; | ||
31 | this.name = "RrdRpnError"; | ||
32 | this.message = (message) ? message : "RPN stack underflow"; | ||
33 | }; | ||
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] = isInfinite(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<doshifts;loop++) { | ||
525 | var shiftstep=1; | ||
526 | if (shifts<0) shiftstep = loop*this.rpnstack[stptr]; | ||
527 | else shiftstep = this.rpnstack[stptr+loop]; | ||
528 | if(shiftstep <0) { | ||
529 | throw new RrdRpnError("negative shift step not allowed: "+shiftstep); | ||
530 | } | ||
531 | shiftstep=Math.ceil(shiftstep/dsstep); | ||
532 | for(var i=0;i<=locstep;i++) { | ||
533 | var offset=shiftstep+i; | ||
534 | if ((offset>=0)&&(offset<output_idx)) { | ||
535 | val = this.rpnp[rpi - 1].data[-dscount * offset]; | ||
536 | if (! isNaN(val)) { | ||
537 | sum+=val; | ||
538 | sum2+=val*val; | ||
539 | count++; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | val=Number.NaN; | ||
545 | if (this.rpnp[rpi].op == RrdRpn.OP_PREDICT) { | ||
546 | if (count>0) 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] = fabs(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 | |||
diff --git a/js/RrdTime.js b/js/RrdTime.js new file mode 100644 index 0000000..95ea0ae --- /dev/null +++ b/js/RrdTime.js | |||
@@ -0,0 +1,621 @@ | |||
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 <manuel.luis at gmail.com> | ||
20 | **/ | ||
21 | |||
22 | "use strict"; | ||
23 | |||
24 | /** | ||
25 | * RrdTimeError | ||
26 | * @constructor | ||
27 | */ | ||
28 | var RrdTimeError = function (message) | ||
29 | { | ||
30 | this.prototype = Error.prototype; | ||
31 | this.name = "RrdTimeError"; | ||
32 | this.message = (message) ? message : "Error"; | ||
33 | }; | ||
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 | case RrdTime.START: | ||
74 | case RrdTime.END: | ||
75 | if (this.tokid === RrdTime.EPOCH) | ||
76 | this.type = RrdTime.RELATIVE_TO_START_TIME; | ||
77 | else | ||
78 | this.type = RrdTime.RELATIVE_TO_END_TIME; | ||
79 | this.tm_sec = 0; | ||
80 | this.tm_min = 0; | ||
81 | this.tm_hour = 0; | ||
82 | this.tm_mday = 0; | ||
83 | this.tm_mon = 0; | ||
84 | this.tm_year = 0; | ||
85 | case RrdTime.NOW: | ||
86 | var time_reference = this.tokid; | ||
87 | this.gettok(); | ||
88 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) | ||
89 | break; | ||
90 | if (time_reference != RrdTime.NOW) { | ||
91 | throw new RrdTimeError("'start' or 'end' MUST be followed by +|- offset"); | ||
92 | } else if (this.tokid != RrdTime.EOF) { | ||
93 | throw new RrdTimeError("if 'now' is followed by a token it must be +|- offset"); | ||
94 | } | ||
95 | break; | ||
96 | case RrdTime.NUMBER: /* Only absolute time specifications below */ | ||
97 | var hour_sv = this.tm_hour; | ||
98 | var year_sv = this.tm_year; | ||
99 | this.tm_hour = 30; | ||
100 | this.tm_year = 30000; | ||
101 | this.tod(); | ||
102 | this.day(); | ||
103 | if (this.tm_hour == 30 && this.tm_year != 30000) | ||
104 | this.tod(); | ||
105 | if (this.tm_hour == 30) | ||
106 | this.tm_hour = hour_sv; | ||
107 | if (this.tm_year == 30000) | ||
108 | this.tm_year = year_sv; | ||
109 | break; | ||
110 | case RrdTime.JAN: | ||
111 | case RrdTime.FEB: | ||
112 | case RrdTime.MAR: | ||
113 | case RrdTime.APR: | ||
114 | case RrdTime.MAY: | ||
115 | case RrdTime.JUN: | ||
116 | case RrdTime.JUL: | ||
117 | case RrdTime.AUG: | ||
118 | case RrdTime.SEP: | ||
119 | case RrdTime.OCT: | ||
120 | case RrdTime.NOV: | ||
121 | case RrdTime.DEC: | ||
122 | this.day(); | ||
123 | if (this.tokid != RrdTime.NUMBER) | ||
124 | break; | ||
125 | this.tod(); | ||
126 | break; | ||
127 | case RrdTime.TEATIME: | ||
128 | hr += 4; | ||
129 | case RrdTime.NOON: | ||
130 | hr += 12; | ||
131 | case RrdTime.MIDNIGHT: | ||
132 | this.tm_hour = hr; | ||
133 | this.tm_min = 0; | ||
134 | this.tm_sec = 0; | ||
135 | this.gettok(); | ||
136 | this.day(); | ||
137 | break; | ||
138 | default: | ||
139 | throw new RrdTimeError("unparsable time: "+this.token+" "+this.sct); | ||
140 | break; | ||
141 | } /* ugly case statement */ | ||
142 | |||
143 | /* | ||
144 | * the OFFSET-SPEC part | ||
145 | * (NOTE, the sc_tokid was prefetched for us by the previous code) | ||
146 | */ | ||
147 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) { | ||
148 | this.specials = RrdTime.TIMEMULTIPLIERS; /* switch special words context */ | ||
149 | while (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS || this.tokid == RrdTime.NUMBER) { | ||
150 | if (this.tokid == RrdTime.NUMBER) { | ||
151 | this.plus_minus(-1); | ||
152 | } else { | ||
153 | this.plus_minus(this.tokid); | ||
154 | } | ||
155 | this.gettok(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */ | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /* now we should be at EOF */ | ||
160 | if (this.tokid != RrdTime.EOF) | ||
161 | throw new RrdTimeError("unparsable trailing text: '..."+this.token+"'"); | ||
162 | // if (this.type == RrdTime.ABSOLUTE_TIME) | ||
163 | // if (mktime(&ptv->tm) == -1) // FIXME ?? | ||
164 | // panic(e("the specified time is incorrect (out of range?)")); | ||
165 | }; | ||
166 | |||
167 | RrdTime.EOF = -1; | ||
168 | RrdTime.MIDNIGHT = 0; | ||
169 | RrdTime.NOON = 1; | ||
170 | RrdTime.TEATIME = 2; | ||
171 | RrdTime.PM = 3; | ||
172 | RrdTime.AM = 4; | ||
173 | RrdTime.YESTERDAY = 5; | ||
174 | RrdTime.TODAY = 6; | ||
175 | RrdTime.TOMORROW = 7; | ||
176 | RrdTime.NOW = 8; | ||
177 | RrdTime.START = 9; | ||
178 | RrdTime.END = 10; | ||
179 | RrdTime.EPOCH = 11; | ||
180 | RrdTime.SECONDS = 12; | ||
181 | RrdTime.MINUTES = 13; | ||
182 | RrdTime.HOURS = 14; | ||
183 | RrdTime.DAYS = 15; | ||
184 | RrdTime.WEEKS = 16; | ||
185 | RrdTime.MONTHS = 17; | ||
186 | RrdTime.YEARS = 18; | ||
187 | RrdTime.MONTHS_MINUTES = 19; | ||
188 | RrdTime.NUMBER = 20; | ||
189 | RrdTime.PLUS = 21; | ||
190 | RrdTime.MINUS = 22; | ||
191 | RrdTime.DOT = 23; | ||
192 | RrdTime.COLON = 24; | ||
193 | RrdTime.SLASH = 25; | ||
194 | RrdTime.ID = 26; | ||
195 | RrdTime.JUNK = 27; | ||
196 | RrdTime.JAN = 28; | ||
197 | RrdTime.FEB = 29; | ||
198 | RrdTime.MAR = 30; | ||
199 | RrdTime.APR = 31; | ||
200 | RrdTime.MAY = 32; | ||
201 | RrdTime.JUN = 33; | ||
202 | RrdTime.JUL = 34; | ||
203 | RrdTime.AUG = 35; | ||
204 | RrdTime.SEP = 36; | ||
205 | RrdTime.OCT = 37; | ||
206 | RrdTime.NOV = 38; | ||
207 | RrdTime.DEC = 39; | ||
208 | RrdTime.SUN = 40; | ||
209 | RrdTime.MON = 41; | ||
210 | RrdTime.TUE = 42; | ||
211 | RrdTime.WED = 43; | ||
212 | RrdTime.THU = 44; | ||
213 | RrdTime.FRI = 45; | ||
214 | RrdTime.SAT = 46; | ||
215 | |||
216 | RrdTime.VARIOUSWORDS = { | ||
217 | "midnight": RrdTime.MIDNIGHT, /* 00:00:00 of today or tomorrow */ | ||
218 | "noon": RrdTime.NOON, /* 12:00:00 of today or tomorrow */ | ||
219 | "teatime": RrdTime.TEATIME, /* 16:00:00 of today or tomorrow */ | ||
220 | "am": RrdTime.AM, /* morning times for 0-12 clock */ | ||
221 | "pm": RrdTime.PM, /* evening times for 0-12 clock */ | ||
222 | "tomorrow": RrdTime.TOMORROW, | ||
223 | "yesterday": RrdTime.YESTERDAY, | ||
224 | "today": RrdTime.TODAY, | ||
225 | "now": RrdTime.NOW, | ||
226 | "n": RrdTime.NOW, | ||
227 | "start": RrdTime.START, | ||
228 | "s": RrdTime.START, | ||
229 | "end": RrdTime.END, | ||
230 | "e": RrdTime.END, | ||
231 | "epoch": RrdTime.EPOCH, | ||
232 | "jan": RrdTime.JAN, | ||
233 | "feb": RrdTime.FEB, | ||
234 | "mar": RrdTime.MAR, | ||
235 | "apr": RrdTime.APR, | ||
236 | "may": RrdTime.MAY, | ||
237 | "jun": RrdTime.JUN, | ||
238 | "jul": RrdTime.JUL, | ||
239 | "aug": RrdTime.AUG, | ||
240 | "sep": RrdTime.SEP, | ||
241 | "oct": RrdTime.OCT, | ||
242 | "nov": RrdTime.NOV, | ||
243 | "dec": RrdTime.DEC, | ||
244 | "january": RrdTime.JAN, | ||
245 | "february": RrdTime.FEB, | ||
246 | "march": RrdTime.MAR, | ||
247 | "april": RrdTime.APR, | ||
248 | // "may": RrdTime.MAY, | ||
249 | "june": RrdTime.JUN, | ||
250 | "july": RrdTime.JUL, | ||
251 | "august": RrdTime.AUG, | ||
252 | "september": RrdTime.SEP, | ||
253 | "october": RrdTime.OCT, | ||
254 | "november": RrdTime.NOV, | ||
255 | "december": RrdTime.DEC, | ||
256 | "sunday": RrdTime.SUN, | ||
257 | "sun": RrdTime.SUN, | ||
258 | "monday": RrdTime.MON, | ||
259 | "mon": RrdTime.MON, | ||
260 | "tuesday": RrdTime.TUE, | ||
261 | "tue": RrdTime.TUE, | ||
262 | "wednesday": RrdTime.WED, | ||
263 | "wed": RrdTime.WED, | ||
264 | "thursday": RrdTime.THU, | ||
265 | "thu": RrdTime.THU, | ||
266 | "friday": RrdTime.FRI, | ||
267 | "fri": RrdTime.FRI, | ||
268 | "saturday": RrdTime.SAT, | ||
269 | "sat": RrdTime.SAT | ||
270 | }; | ||
271 | |||
272 | RrdTime.TIMEMULTIPLIERS = { | ||
273 | "second": RrdTime.SECONDS, /* seconds multiplier */ | ||
274 | "seconds": RrdTime.SECONDS, /* (pluralized) */ | ||
275 | "sec": RrdTime.SECONDS, /* (generic) */ | ||
276 | "s": RrdTime.SECONDS, /* (short generic) */ | ||
277 | "minute": RrdTime.MINUTES, /* minutes multiplier */ | ||
278 | "minutes": RrdTime.MINUTES, /* (pluralized) */ | ||
279 | "min": RrdTime.MINUTES, /* (generic) */ | ||
280 | "m": RrdTime.MONTHS_MINUTES, /* (short generic) */ | ||
281 | "hour": RrdTime.HOURS, /* hours ... */ | ||
282 | "hours": RrdTime.HOURS, /* (pluralized) */ | ||
283 | "hr": RrdTime.HOURS, /* (generic) */ | ||
284 | "h": RrdTime.HOURS, /* (short generic) */ | ||
285 | "day": RrdTime.DAYS, /* days ... */ | ||
286 | "days": RrdTime.DAYS, /* (pluralized) */ | ||
287 | "d": RrdTime.DAYS, /* (short generic) */ | ||
288 | "week": RrdTime.WEEKS, /* week ... */ | ||
289 | "weeks": RrdTime.WEEKS, /* (pluralized) */ | ||
290 | "wk": RrdTime.WEEKS, /* (generic) */ | ||
291 | "w": RrdTime.WEEKS, /* (short generic) */ | ||
292 | "month": RrdTime.MONTHS, /* week ... */ | ||
293 | "months": RrdTime.MONTHS, /* (pluralized) */ | ||
294 | "mon": RrdTime.MONTHS, /* (generic) */ | ||
295 | "year": RrdTime.YEARS, /* year ... */ | ||
296 | "years": RrdTime.YEARS, /* (pluralized) */ | ||
297 | "yr": RrdTime.YEARS, /* (generic) */ | ||
298 | "y": RrdTime.YEARS /* (short generic) */ | ||
299 | }; | ||
300 | |||
301 | RrdTime.ABSOLUTE_TIME = 0; | ||
302 | RrdTime.RELATIVE_TO_START_TIME = 1; | ||
303 | RrdTime.RELATIVE_TO_END_TIME = 2; | ||
304 | RrdTime.RELATIVE_TO_EPOCH = 3; | ||
305 | |||
306 | RrdTime.prototype.gettok = function () | ||
307 | { | ||
308 | if (this.tokidx >= this.toklen) { | ||
309 | this.tokid = RrdTime.EOF; | ||
310 | } else { | ||
311 | this.token = this.tokens[this.tokidx]; | ||
312 | this.tokidx++; | ||
313 | if (!isNaN(this.token)) { | ||
314 | this.tokid = RrdTime.NUMBER; | ||
315 | this.token = parseInt(this.token, 10); | ||
316 | } else if (this.token === ':') { | ||
317 | this.tokid = RrdTime.COLON; | ||
318 | } else if (this.token === '.') { | ||
319 | this.tokid = RrdTime.DOT; | ||
320 | } else if (this.token === '+') { | ||
321 | this.tokid = RrdTime.PLUS; | ||
322 | } else if (this.token === '/') { | ||
323 | this.tokid = RrdTime.SLASH; | ||
324 | } else if (this.token === '-') { | ||
325 | this.tokid = RrdTime.MINUS; | ||
326 | } else { | ||
327 | this.tokid = RrdTime.ID; | ||
328 | if (this.token in this.specials) | ||
329 | this.tokid = this.specials[this.token]; | ||
330 | } | ||
331 | } | ||
332 | return this.tokid; | ||
333 | }; | ||
334 | |||
335 | RrdTime.prototype.plus_minus = function (doop) | ||
336 | { | ||
337 | var op = RrdTime.PLUS; | ||
338 | var prev_multiplier = -1; | ||
339 | var delta; | ||
340 | |||
341 | if (doop >= 0) { | ||
342 | op = doop; | ||
343 | if (this.gettok() != RrdTime.NUMBER) | ||
344 | throw new RrdTimeError("There should be number after '"+(op == RrdTime.PLUS ? '+' : '-')+"'"); | ||
345 | prev_multiplier = -1; /* reset months-minutes guessing mechanics */ | ||
346 | } | ||
347 | /* if doop is < 0 then we repeat the previous op with the prefetched number */ | ||
348 | delta = this.token; | ||
349 | if (this.gettok() == RrdTime.MONTHS_MINUTES) { | ||
350 | /* hard job to guess what does that -5m means: -5mon or -5min? */ | ||
351 | switch (prev_multiplier) { | ||
352 | case RrdTime.DAYS: | ||
353 | case RrdTime.WEEKS: | ||
354 | case RrdTime.MONTHS: | ||
355 | case RrdTime.YEARS: | ||
356 | this.tokid = RrdTime.MONTHS; | ||
357 | break; | ||
358 | case RrdTime.SECONDS: | ||
359 | case RrdTime.MINUTES: | ||
360 | case RrdTime.HOURS: | ||
361 | this.tokid = RrdTime.MINUTES; | ||
362 | break; | ||
363 | default: | ||
364 | if (delta < 6) /* it may be some other value but in the context of RRD who needs less than 6 min deltas? */ | ||
365 | this.tokid = RrdTime.MONTHS; | ||
366 | else | ||
367 | this.tokid = RrdTime.MINUTES; | ||
368 | } | ||
369 | } | ||
370 | prev_multiplier = this.tokid; | ||
371 | switch (this.tokid) { | ||
372 | case RrdTime.YEARS: | ||
373 | this.tm_year += ( op == RrdTime.PLUS) ? delta : -delta; | ||
374 | return; | ||
375 | case RrdTime.MONTHS: | ||
376 | this.tm_mon += ( op == RrdTime.PLUS) ? delta : -delta; | ||
377 | return; | ||
378 | case RrdTime.WEEKS: | ||
379 | delta *= 7; | ||
380 | case RrdTime.DAYS: | ||
381 | this.tm_mday += ( op == RrdTime.PLUS) ? delta : -delta; | ||
382 | return; | ||
383 | case RrdTime.HOURS: | ||
384 | this.offset += (op == RrdTime.PLUS) ? delta * 60 * 60 : -delta * 60 * 60; | ||
385 | return; | ||
386 | case RrdTime.MINUTES: | ||
387 | this.offset += (op == RrdTime.PLUS) ? delta * 60 : -delta * 60; | ||
388 | return; | ||
389 | case RrdTime.SECONDS: | ||
390 | this.offset += (op == RrdTime.PLUS) ? delta : -delta; | ||
391 | return; | ||
392 | default: /*default unit is seconds */ | ||
393 | this.offset += (op == RrdTime.PLUS) ? delta : -delta; | ||
394 | return; | ||
395 | } | ||
396 | throw new RrdTimeError("well-known time unit expected after "+delta); | ||
397 | }; | ||
398 | |||
399 | RrdTime.prototype.tod = function() /* tod() computes the time of day (TIME-OF-DAY-SPEC) */ | ||
400 | { | ||
401 | var hour, minute = 0; | ||
402 | var tlen; | ||
403 | /* save token status in case we must abort */ | ||
404 | var tokid_sv = this.tokid; | ||
405 | |||
406 | tlen = (this.token+"").length; | ||
407 | /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */ | ||
408 | if (tlen > 2) | ||
409 | return; | ||
410 | hour = this.token; | ||
411 | this.gettok(); | ||
412 | if (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT) { | ||
413 | /* guess we are looking at a date */ | ||
414 | this.tokid = tokid_sv; | ||
415 | this.token = hour; | ||
416 | return; | ||
417 | } | ||
418 | if (this.tokid == RrdTime.COLON) { | ||
419 | if (this.gettok() != RrdTime.NUMBER) | ||
420 | throw new RrdTimeError("Parsing HH:MM syntax, expecting MM as number, got none"); | ||
421 | minute = this.token; | ||
422 | if (minute > 59) | ||
423 | throw new RrdTimeError("parsing HH:MM syntax, got MM = "+minute+" (>59!)"); | ||
424 | this.gettok(); | ||
425 | } | ||
426 | /* check if an AM or PM specifier was given */ | ||
427 | if (this.tokid == RrdTime.AM || this.tokid == RrdTime.PM) { | ||
428 | if (hour > 12) { | ||
429 | throw new RrdTimeError("there cannot be more than 12 AM or PM hours"); | ||
430 | } | ||
431 | if (this.tokid == RrdTime.PM) { | ||
432 | if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ | ||
433 | hour += 12; | ||
434 | } else { | ||
435 | if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ | ||
436 | hour = 0; | ||
437 | } | ||
438 | this.gettok(); | ||
439 | } else if (hour > 23) { | ||
440 | /* guess it was not a time then ... */ | ||
441 | this.tokid = tokid_sv; | ||
442 | this.token = hour; | ||
443 | return; | ||
444 | } | ||
445 | this.tm_hour = hour; | ||
446 | this.tm_min = minute; | ||
447 | this.tm_sec = 0; | ||
448 | if (this.tm_hour == 24) { | ||
449 | this.tm_hour = 0; | ||
450 | this.tm_mday++; | ||
451 | } | ||
452 | }; | ||
453 | |||
454 | RrdTime.prototype.assign_date = function(mday, mon, year) | ||
455 | { | ||
456 | if (year > 138) { | ||
457 | if (year > 1970) { | ||
458 | year -= 1900; | ||
459 | } else { | ||
460 | throw new RrdTimeError("invalid year "+year+" (should be either 00-99 or >1900)"); | ||
461 | } | ||
462 | } else if (year >= 0 && year < 38) { | ||
463 | year += 100; /* Allow year 2000-2037 to be specified as */ | ||
464 | } | ||
465 | /* 00-37 until the problem of 2038 year will */ | ||
466 | /* arise for unices with 32-bit time_t :) */ | ||
467 | if (year < 70) | ||
468 | throw new RrdTimeError("won't handle dates before epoch (01/01/1970), sorry"); | ||
469 | |||
470 | this.tm_mday = mday; | ||
471 | this.tm_mon = mon; | ||
472 | this.tm_year = year; | ||
473 | }; | ||
474 | |||
475 | RrdTime.prototype.day = function () | ||
476 | { | ||
477 | var mday = 0, wday, mon, year = this.tm_year; | ||
478 | var tlen; | ||
479 | |||
480 | switch (this.tokid) { | ||
481 | case RrdTime.YESTERDAY: | ||
482 | this.tm_mday--; | ||
483 | case RrdTime.TODAY: | ||
484 | this.gettok(); | ||
485 | break; | ||
486 | case RrdTime.TOMORROW: | ||
487 | this.tm_mday++; | ||
488 | this.gettok(); | ||
489 | break; | ||
490 | case RrdTime.JAN: | ||
491 | case RrdTime.FEB: | ||
492 | case RrdTime.MAR: | ||
493 | case RrdTime.APR: | ||
494 | case RrdTime.MAY: | ||
495 | case RrdTime.JUN: | ||
496 | case RrdTime.JUL: | ||
497 | case RrdTime.AUG: | ||
498 | case RrdTime.SEP: | ||
499 | case RrdTime.OCT: | ||
500 | case RrdTime.NOV: | ||
501 | case RrdTime.DEC: | ||
502 | mon = (this.tokid - RrdTime.JAN); | ||
503 | if (this.gettok() != RrdTime.NUMBER) | ||
504 | throw new RrdTimeError("the day of the month should follow month name"); | ||
505 | mday = this.token; | ||
506 | if (this.gettok() == RrdTime.NUMBER) { | ||
507 | year = this.token; | ||
508 | this.gettok(); | ||
509 | } else { | ||
510 | year = this.tm_year; | ||
511 | } | ||
512 | this.assign_date(mday, mon, year); | ||
513 | break; | ||
514 | case RrdTime.SUN: | ||
515 | case RrdTime.MON: | ||
516 | case RrdTime.TUE: | ||
517 | case RrdTime.WED: | ||
518 | case RrdTime.THU: | ||
519 | case RrdTime.FRI: | ||
520 | case RrdTime.SAT: | ||
521 | wday = (this.tokid - RrdTime.SUN); | ||
522 | this.tm_mday += (wday - this.tm_wday); | ||
523 | this.gettok(); | ||
524 | break; | ||
525 | case RrdTime.NUMBER: | ||
526 | mon = this.token; | ||
527 | if (mon > 10 * 365 * 24 * 60 * 60) { | ||
528 | this.localtime(mon); | ||
529 | this.gettok(); | ||
530 | break; | ||
531 | } | ||
532 | if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */ | ||
533 | var str = this.token + ''; | ||
534 | year = parseInt(str.substr(0,4),10); | ||
535 | mon = parseInt(str.substr(4,2),10); | ||
536 | mday = parseInt(str.substr(6,2),10); | ||
537 | this.gettok(); | ||
538 | } else { | ||
539 | this.gettok(); | ||
540 | if (mon <= 31 && (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT)) { | ||
541 | var sep = this.tokid; | ||
542 | if (this.gettok() != RrdTime.NUMBER) | ||
543 | throw new RrdTimeError("there should be "+(RrdTime.DOT ? "month" : "day")+" number after '"+(RrdTime.DOT ? '.' : '/')+"'"); | ||
544 | mday = this.token; | ||
545 | if (this.gettok() == sep) { | ||
546 | if (this.gettok() != RrdTime.NUMBER) | ||
547 | throw new RrdTimeError("there should be year number after '"+(sep == RrdTime.DOT ? '.' : '/')+"'"); | ||
548 | year = this.token; | ||
549 | this.gettok(); | ||
550 | } | ||
551 | if (sep == RrdTime.DOT) { | ||
552 | var x = mday; | ||
553 | mday = mon; | ||
554 | mon = x; | ||
555 | } | ||
556 | } | ||
557 | } | ||
558 | mon--; | ||
559 | if (mon < 0 || mon > 11) | ||
560 | throw new RrdTimeError("did you really mean month "+(mon+1)+"?"); | ||
561 | if (mday < 1 || mday > 31) | ||
562 | throw new RrdTimeError("I'm afraid that "+mday+" is not a valid day of the month"); | ||
563 | this.assign_date(mday, mon, year); | ||
564 | break; | ||
565 | } | ||
566 | }; | ||
567 | |||
568 | RrdTime.prototype.localtime = function (tm) | ||
569 | { | ||
570 | var date = new Date(tm*1000); | ||
571 | this.tm_sec = date.getSeconds(); | ||
572 | this.tm_min = date.getMinutes(); | ||
573 | this.tm_hour = date.getHours(); | ||
574 | this.tm_mday = date.getDate(); | ||
575 | this.tm_mon = date.getMonth(); | ||
576 | this.tm_year = date.getFullYear()-1900; | ||
577 | this.tm_wday = date.getDay(); | ||
578 | }; | ||
579 | |||
580 | RrdTime.prototype.mktime = function() | ||
581 | { | ||
582 | var date = new Date(this.tm_year+1900, this.tm_mon, this.tm_mday, this.tm_hour, this.tm_min, this.tm_sec); | ||
583 | return Math.round(date.getTime()/1000.0); | ||
584 | }; | ||
585 | |||
586 | RrdTime.proc_start_end = function(start_t, end_t) | ||
587 | { | ||
588 | var start, end; | ||
589 | |||
590 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME && end_t.type == RrdTime.RELATIVE_TO_START_TIME) | ||
591 | throw new RrdTimeError("the start and end times cannot be specified relative to each other"); | ||
592 | if (start_t.type == RrdTime.RELATIVE_TO_START_TIME) | ||
593 | throw new RrdTimeError("the start time cannot be specified relative to itself"); | ||
594 | if (end_t.type == RrdTime.RELATIVE_TO_END_TIME) | ||
595 | throw new RrdTimeError("the end time cannot be specified relative to itself"); | ||
596 | |||
597 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME) { | ||
598 | end = end_t.mktime() + end_t.offset; | ||
599 | var tmtmp = new Date(end*1000); | ||
600 | tmtmp.setDate(tmtmp.getDate()+start_t.tm_mday); | ||
601 | tmtmp.setMonth(tmtmp.getMonth()+start_t.tm_mon); | ||
602 | tmtmp.setFullYear(tmtmp.getFullYear()+start_t.tm_year); | ||
603 | start = Math.round(tmtmp.getTime()/1000.0) + start_t.offset; | ||
604 | } else { | ||
605 | start = start_t.mktime() + start_t.offset; | ||
606 | } | ||
607 | |||
608 | if (end_t.type == RrdTime.RELATIVE_TO_START_TIME) { | ||
609 | start = start_t.mktime() + start_t.offset; | ||
610 | var tmtmp = new Date(start*1000); | ||
611 | tmtmp.setDate(tmtmp.getDate()+end_t.tm_mday); | ||
612 | tmtmp.setMonth(tmtmp.getMonth()+end_t.tm_mon); | ||
613 | tmtmp.setFullYear(tmtmp.getFullYear()+end_t.tm_year); | ||
614 | end = Math.round(tmtmp.getTime()/1000.0) + end_t.offset; | ||
615 | } else { | ||
616 | end = end_t.mktime() + end_t.offset; | ||
617 | } | ||
618 | |||
619 | return [start, end]; | ||
620 | }; | ||
621 | |||
diff --git a/js/base64.js b/js/base64.js new file mode 100644 index 0000000..7d9536a --- /dev/null +++ b/js/base64.js | |||
@@ -0,0 +1,143 @@ | |||
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 = 0; | ||
117 | |||
118 | while ( i < utftext.length ) { | ||
119 | |||
120 | c = utftext.charCodeAt(i); | ||
121 | |||
122 | if (c < 128) { | ||
123 | string += String.fromCharCode(c); | ||
124 | i++; | ||
125 | } | ||
126 | else if((c > 191) && (c < 224)) { | ||
127 | c2 = utftext.charCodeAt(i+1); | ||
128 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); | ||
129 | i += 2; | ||
130 | } | ||
131 | else { | ||
132 | c2 = utftext.charCodeAt(i+1); | ||
133 | c3 = utftext.charCodeAt(i+2); | ||
134 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); | ||
135 | i += 3; | ||
136 | } | ||
137 | |||
138 | } | ||
139 | |||
140 | return string; | ||
141 | } | ||
142 | |||
143 | } | ||
diff --git a/js/binaryXHR.js b/js/binaryXHR.js new file mode 100644 index 0000000..d91ad72 --- /dev/null +++ b/js/binaryXHR.js | |||
@@ -0,0 +1,234 @@ | |||
1 | |||
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 | this.message=msg; | ||
20 | this.name="Invalid BinaryFile"; | ||
21 | } | ||
22 | |||
23 | // pretty print | ||
24 | InvalidBinaryFile.prototype.toString = function() { | ||
25 | return this.name + ': "' + this.message + '"'; | ||
26 | } | ||
27 | |||
28 | // ===================================================================== | ||
29 | // BinaryFile class | ||
30 | // Allows access to element inside a binary stream | ||
31 | function BinaryFile(strData, iDataOffset, iDataLength) { | ||
32 | var data = strData; | ||
33 | var dataOffset = iDataOffset || 0; | ||
34 | var dataLength = 0; | ||
35 | // added | ||
36 | var doubleMantExpHi=Math.pow(2,-28); | ||
37 | var doubleMantExpLo=Math.pow(2,-52); | ||
38 | var doubleMantExpFast=Math.pow(2,-20); | ||
39 | |||
40 | this.getRawData = function() { | ||
41 | return data; | ||
42 | } | ||
43 | |||
44 | if (typeof strData == "string") { | ||
45 | dataLength = iDataLength || data.length; | ||
46 | |||
47 | this.getByteAt = function(iOffset) { | ||
48 | return data.charCodeAt(iOffset + dataOffset) & 0xFF; | ||
49 | } | ||
50 | } else if (typeof strData == "unknown") { | ||
51 | dataLength = iDataLength || IEBinary_getLength(data); | ||
52 | |||
53 | this.getByteAt = function(iOffset) { | ||
54 | return IEBinary_getByteAt(data, iOffset + dataOffset); | ||
55 | } | ||
56 | } else { | ||
57 | throw new InvalidBinaryFile("Unsupported type " + (typeof strData)); | ||
58 | } | ||
59 | |||
60 | this.getLength = function() { | ||
61 | return dataLength; | ||
62 | } | ||
63 | |||
64 | this.getSByteAt = function(iOffset) { | ||
65 | var iByte = this.getByteAt(iOffset); | ||
66 | if (iByte > 127) | ||
67 | return iByte - 256; | ||
68 | else | ||
69 | return iByte; | ||
70 | } | ||
71 | |||
72 | this.getShortAt = function(iOffset) { | ||
73 | var iShort = (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) | ||
74 | if (iShort < 0) iShort += 65536; | ||
75 | return iShort; | ||
76 | } | ||
77 | this.getSShortAt = function(iOffset) { | ||
78 | var iUShort = this.getShortAt(iOffset); | ||
79 | if (iUShort > 32767) | ||
80 | return iUShort - 65536; | ||
81 | else | ||
82 | return iUShort; | ||
83 | } | ||
84 | this.getLongAt = function(iOffset) { | ||
85 | var iByte1 = this.getByteAt(iOffset), | ||
86 | iByte2 = this.getByteAt(iOffset + 1), | ||
87 | iByte3 = this.getByteAt(iOffset + 2), | ||
88 | iByte4 = this.getByteAt(iOffset + 3); | ||
89 | |||
90 | var iLong = (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; | ||
91 | if (iLong < 0) iLong += 4294967296; | ||
92 | return iLong; | ||
93 | } | ||
94 | this.getSLongAt = function(iOffset) { | ||
95 | var iULong = this.getLongAt(iOffset); | ||
96 | if (iULong > 2147483647) | ||
97 | return iULong - 4294967296; | ||
98 | else | ||
99 | return iULong; | ||
100 | } | ||
101 | this.getStringAt = function(iOffset, iLength) { | ||
102 | var aStr = []; | ||
103 | for (var i=iOffset,j=0;i<iOffset+iLength;i++,j++) { | ||
104 | aStr[j] = String.fromCharCode(this.getByteAt(i)); | ||
105 | } | ||
106 | return aStr.join(""); | ||
107 | } | ||
108 | |||
109 | // Added | ||
110 | this.getCStringAt = function(iOffset, iMaxLength) { | ||
111 | var aStr = []; | ||
112 | for (var i=iOffset,j=0;(i<iOffset+iMaxLength) && (this.getByteAt(i)>0);i++,j++) { | ||
113 | aStr[j] = String.fromCharCode(this.getByteAt(i)); | ||
114 | } | ||
115 | return aStr.join(""); | ||
116 | } | ||
117 | |||
118 | // Added | ||
119 | this.getDoubleAt = function(iOffset) { | ||
120 | var iByte1 = this.getByteAt(iOffset), | ||
121 | iByte2 = this.getByteAt(iOffset + 1), | ||
122 | iByte3 = this.getByteAt(iOffset + 2), | ||
123 | iByte4 = this.getByteAt(iOffset + 3), | ||
124 | iByte5 = this.getByteAt(iOffset + 4), | ||
125 | iByte6 = this.getByteAt(iOffset + 5), | ||
126 | iByte7 = this.getByteAt(iOffset + 6), | ||
127 | iByte8 = this.getByteAt(iOffset + 7); | ||
128 | var iSign=iByte8 >> 7; | ||
129 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); | ||
130 | var iMantHi=((((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5) << 8) + iByte4; | ||
131 | var iMantLo=((((iByte3) << 8) + iByte2) << 8) + iByte1; | ||
132 | |||
133 | if (iExpRaw==0) return 0.0; | ||
134 | if (iExpRaw==0x7ff) return undefined; | ||
135 | |||
136 | var iExp=(iExpRaw & 0x7FF)-1023; | ||
137 | |||
138 | var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMantLo*doubleMantExpLo + iMantHi*doubleMantExpHi); | ||
139 | return dDouble; | ||
140 | } | ||
141 | // added | ||
142 | // Extracts only 4 bytes out of 8, loosing in precision (20 bit mantissa) | ||
143 | this.getFastDoubleAt = function(iOffset) { | ||
144 | var iByte5 = this.getByteAt(iOffset + 4), | ||
145 | iByte6 = this.getByteAt(iOffset + 5), | ||
146 | iByte7 = this.getByteAt(iOffset + 6), | ||
147 | iByte8 = this.getByteAt(iOffset + 7); | ||
148 | var iSign=iByte8 >> 7; | ||
149 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); | ||
150 | var iMant=((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5; | ||
151 | |||
152 | if (iExpRaw==0) return 0.0; | ||
153 | if (iExpRaw==0x7ff) return undefined; | ||
154 | |||
155 | var iExp=(iExpRaw & 0x7FF)-1023; | ||
156 | |||
157 | var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMant*doubleMantExpFast); | ||
158 | return dDouble; | ||
159 | } | ||
160 | |||
161 | this.getCharAt = function(iOffset) { | ||
162 | return String.fromCharCode(this.getByteAt(iOffset)); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | |||
167 | document.write( | ||
168 | "<script type='text/vbscript'>\r\n" | ||
169 | + "Function IEBinary_getByteAt(strBinary, iOffset)\r\n" | ||
170 | + " IEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\n" | ||
171 | + "End Function\r\n" | ||
172 | + "Function IEBinary_getLength(strBinary)\r\n" | ||
173 | + " IEBinary_getLength = LenB(strBinary)\r\n" | ||
174 | + "End Function\r\n" | ||
175 | + "</script>\r\n" | ||
176 | ); | ||
177 | |||
178 | |||
179 | |||
180 | // =============================================================== | ||
181 | // Load a binary file from the specified URL | ||
182 | // Will return an object of type BinaryFile | ||
183 | function FetchBinaryURL(url) { | ||
184 | var request = new XMLHttpRequest(); | ||
185 | request.open("GET", url,false); | ||
186 | try { | ||
187 | request.overrideMimeType('text/plain; charset=x-user-defined'); | ||
188 | } catch (err) { | ||
189 | // ignore any error, just to make both FF and IE work | ||
190 | } | ||
191 | request.send(null); | ||
192 | |||
193 | var response=request.responseBody; | ||
194 | if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE | ||
195 | response=request.responseText; | ||
196 | } | ||
197 | var bf=new BinaryFile(response); | ||
198 | return bf; | ||
199 | } | ||
200 | |||
201 | |||
202 | // =============================================================== | ||
203 | // Asyncronously load a binary file from the specified URL | ||
204 | // | ||
205 | // callback must be a function with one or two arguments: | ||
206 | // - bf = an object of type BinaryFile | ||
207 | // - optional argument object (used only if callback_arg not undefined) | ||
208 | function FetchBinaryURLAsync(url, callback, callback_arg) { | ||
209 | var callback_wrapper = function() { | ||
210 | if(this.readyState == 4) { | ||
211 | var response=this.responseBody; | ||
212 | if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE | ||
213 | response=this.responseText; | ||
214 | } | ||
215 | var bf=new BinaryFile(response); | ||
216 | if (callback_arg!=null) { | ||
217 | callback(bf,callback_arg); | ||
218 | } else { | ||
219 | callback(bf); | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | |||
224 | var request = new XMLHttpRequest(); | ||
225 | request.onreadystatechange = callback_wrapper; | ||
226 | request.open("GET", url,true); | ||
227 | try { | ||
228 | request.overrideMimeType('text/plain; charset=x-user-defined'); | ||
229 | } catch (err) { | ||
230 | // ignore any error, just to make both FF and IE work | ||
231 | } | ||
232 | request.send(null); | ||
233 | return request | ||
234 | } | ||
diff --git a/js/rrdFile.js b/js/rrdFile.js new file mode 100644 index 0000000..870c88e --- /dev/null +++ b/js/rrdFile.js | |||
@@ -0,0 +1,408 @@ | |||
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 | rrd_align,row_cnt,pdp_step,my_idx) { | ||
93 | this.rrd_data=rrd_data; | ||
94 | this.rra_def_idx=rra_def_idx; | ||
95 | this.rrd_align=rrd_align; | ||
96 | this.row_cnt=row_cnt; | ||
97 | this.pdp_step=pdp_step; | ||
98 | this.my_idx=my_idx; | ||
99 | } | ||
100 | |||
101 | RRDRRAInfo.prototype.getIdx = function() { | ||
102 | return this.my_idx; | ||
103 | } | ||
104 | |||
105 | // Get number of rows | ||
106 | RRDRRAInfo.prototype.getNrRows = function() { | ||
107 | return this.row_cnt; | ||
108 | } | ||
109 | |||
110 | // Get number of slots used for consolidation | ||
111 | // Mostly for internal use | ||
112 | RRDRRAInfo.prototype.getPdpPerRow = function() { | ||
113 | if (this.rrd_align==32) | ||
114 | return this.rrd_data.getLongAt(this.rra_def_idx+24,20); | ||
115 | else | ||
116 | return this.rrd_data.getLongAt(this.rra_def_idx+32,20); | ||
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<this.row_cnt)) { | ||
151 | if ((ds_idx>=0) && (ds_idx<ds_cnt)){ | ||
152 | // it is round robin, starting from cur_row+1 | ||
153 | var real_row_idx=row_idx+this.cur_row+1; | ||
154 | if (real_row_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.load_header(); | ||
203 | this.calc_idxs(); | ||
204 | } | ||
205 | |||
206 | // Internal, used for initialization | ||
207 | RRDHeader.prototype.validate_rrd = function() { | ||
208 | if (this.rrd_data.getCStringAt(0,4)!=="RRD") throw new InvalidRRD("Wrong magic id."); | ||
209 | |||
210 | this.rrd_version=this.rrd_data.getCStringAt(4,5); | ||
211 | if ((this.rrd_version!=="0003")&&(this.rrd_version!=="0004")) { | ||
212 | throw new InvalidRRD("Unsupported RRD version "+this.rrd_version+"."); | ||
213 | } | ||
214 | |||
215 | if (this.rrd_data.getDoubleAt(12)==8.642135e+130) { | ||
216 | this.rrd_align=32; | ||
217 | } else if (this.rrd_data.getDoubleAt(16)==8.642135e+130) { | ||
218 | this.rrd_align=64; | ||
219 | } else { | ||
220 | throw new InvalidRRD("Unsupported platform."); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | // Internal, used for initialization | ||
225 | RRDHeader.prototype.load_header = function() { | ||
226 | if (this.rrd_align==32) { | ||
227 | this.ds_cnt=this.rrd_data.getLongAt(20,false); | ||
228 | this.rra_cnt=this.rrd_data.getLongAt(24,false); | ||
229 | this.pdp_step=this.rrd_data.getLongAt(28,false); | ||
230 | // 8*10 unused values follow | ||
231 | this.top_header_size=112; | ||
232 | } else { | ||
233 | //get only the low 32 bits, the high 32 should always be 0 | ||
234 | this.ds_cnt=this.rrd_data.getLongAt(24,false); | ||
235 | this.rra_cnt=this.rrd_data.getLongAt(32,false); | ||
236 | this.pdp_step=this.rrd_data.getLongAt(40,false); | ||
237 | // 8*10 unused values follow | ||
238 | this.top_header_size=128; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | // Internal, used for initialization | ||
243 | RRDHeader.prototype.calc_idxs = function() { | ||
244 | this.ds_def_idx=this.top_header_size; | ||
245 | // char ds_nam[20], char dst[20], unival par[10] | ||
246 | this.ds_el_size=120; | ||
247 | |||
248 | this.rra_def_idx=this.ds_def_idx+this.ds_el_size*this.ds_cnt; | ||
249 | // char cf_nam[20], uint row_cnt, uint pdp_cnt, unival par[10] | ||
250 | this.row_cnt_idx; | ||
251 | if (this.rrd_align==32) { | ||
252 | this.rra_def_el_size=108; | ||
253 | this.row_cnt_idx=20; | ||
254 | } else { | ||
255 | this.rra_def_el_size=120; | ||
256 | this.row_cnt_idx=24; | ||
257 | } | ||
258 | |||
259 | this.live_head_idx=this.rra_def_idx+this.rra_def_el_size*this.rra_cnt; | ||
260 | // time_t last_up, int last_up_usec | ||
261 | if (this.rrd_align==32) { | ||
262 | this.live_head_size=8; | ||
263 | } else { | ||
264 | this.live_head_size=16; | ||
265 | } | ||
266 | |||
267 | this.pdp_prep_idx=this.live_head_idx+this.live_head_size; | ||
268 | // char last_ds[30], unival scratch[10] | ||
269 | this.pdp_prep_el_size=112; | ||
270 | |||
271 | this.cdp_prep_idx=this.pdp_prep_idx+this.pdp_prep_el_size*this.ds_cnt; | ||
272 | // unival scratch[10] | ||
273 | this.cdp_prep_el_size=80; | ||
274 | |||
275 | this.rra_ptr_idx=this.cdp_prep_idx+this.cdp_prep_el_size*this.ds_cnt*this.rra_cnt; | ||
276 | // uint cur_row | ||
277 | if (this.rrd_align==32) { | ||
278 | this.rra_ptr_el_size=4; | ||
279 | } else { | ||
280 | this.rra_ptr_el_size=8; | ||
281 | } | ||
282 | |||
283 | this.header_size=this.rra_ptr_idx+this.rra_ptr_el_size*this.rra_cnt; | ||
284 | } | ||
285 | |||
286 | // Optional initialization | ||
287 | // Read and calculate row counts | ||
288 | RRDHeader.prototype.load_row_cnts = function() { | ||
289 | this.rra_def_row_cnts=[]; | ||
290 | this.rra_def_row_cnt_sums=[]; // how many rows before me | ||
291 | for (var i=0; i<this.rra_cnt; i++) { | ||
292 | this.rra_def_row_cnts[i]=this.rrd_data.getLongAt(this.rra_def_idx+i*this.rra_def_el_size+this.row_cnt_idx,false); | ||
293 | if (i==0) { | ||
294 | this.rra_def_row_cnt_sums[i]=0; | ||
295 | } else { | ||
296 | this.rra_def_row_cnt_sums[i]=this.rra_def_row_cnt_sums[i-1]+this.rra_def_row_cnts[i-1]; | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | // --------------------------- | ||
302 | // Start of user functions | ||
303 | |||
304 | RRDHeader.prototype.getMinStep = function() { | ||
305 | return this.pdp_step; | ||
306 | } | ||
307 | RRDHeader.prototype.getLastUpdate = function() { | ||
308 | return this.rrd_data.getLongAt(this.live_head_idx,false); | ||
309 | } | ||
310 | |||
311 | RRDHeader.prototype.getNrDSs = function() { | ||
312 | return this.ds_cnt; | ||
313 | } | ||
314 | RRDHeader.prototype.getDSNames = function() { | ||
315 | var ds_names=[] | ||
316 | for (var idx=0; idx<this.ds_cnt; idx++) { | ||
317 | var ds=this.getDSbyIdx(idx); | ||
318 | var ds_name=ds.getName() | ||
319 | ds_names.push(ds_name); | ||
320 | } | ||
321 | return ds_names; | ||
322 | } | ||
323 | RRDHeader.prototype.getDSbyIdx = function(idx) { | ||
324 | if ((idx>=0) && (idx<this.ds_cnt)) { | ||
325 | return new RRDDS(this.rrd_data,this.ds_def_idx+this.ds_el_size*idx,idx); | ||
326 | } else { | ||
327 | throw RangeError("DS idx ("+ idx +") out of range [0-" + this.ds_cnt +")."); | ||
328 | } | ||
329 | } | ||
330 | RRDHeader.prototype.getDSbyName = function(name) { | ||
331 | for (var idx=0; idx<this.ds_cnt; idx++) { | ||
332 | var ds=this.getDSbyIdx(idx); | ||
333 | var ds_name=ds.getName() | ||
334 | if (ds_name==name) | ||
335 | return ds; | ||
336 | } | ||
337 | throw RangeError("DS name "+ name +" unknown."); | ||
338 | } | ||
339 | |||
340 | RRDHeader.prototype.getNrRRAs = function() { | ||
341 | return this.rra_cnt; | ||
342 | } | ||
343 | RRDHeader.prototype.getRRAInfo = function(idx) { | ||
344 | if ((idx>=0) && (idx<this.rra_cnt)) { | ||
345 | return new RRDRRAInfo(this.rrd_data, | ||
346 | this.rra_def_idx+idx*this.rra_def_el_size, | ||
347 | this.rrd_align,this.rra_def_row_cnts[idx],this.pdp_step, | ||
348 | idx); | ||
349 | } else { | ||
350 | throw RangeError("RRA idx ("+ idx +") out of range [0-" + this.rra_cnt +")."); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | // ============================================================ | ||
355 | // RRDFile class | ||
356 | // Given a BinaryFile, gives access to the RRD archive fields | ||
357 | // | ||
358 | // Arguments: | ||
359 | // bf must be an object compatible with the BinaryFile interface | ||
360 | function RRDFile(bf) { | ||
361 | var rrd_data=bf | ||
362 | |||
363 | this.rrd_header=new RRDHeader(rrd_data); | ||
364 | this.rrd_header.load_row_cnts(); | ||
365 | |||
366 | // =================================== | ||
367 | // Start of user functions | ||
368 | |||
369 | this.getMinStep = function() { | ||
370 | return this.rrd_header.getMinStep(); | ||
371 | } | ||
372 | this.getLastUpdate = function() { | ||
373 | return this.rrd_header.getLastUpdate(); | ||
374 | } | ||
375 | |||
376 | this.getNrDSs = function() { | ||
377 | return this.rrd_header.getNrDSs(); | ||
378 | } | ||
379 | this.getDSNames = function() { | ||
380 | return this.rrd_header.getDSNames(); | ||
381 | } | ||
382 | this.getDS = function(id) { | ||
383 | if (typeof id == "number") { | ||
384 | return this.rrd_header.getDSbyIdx(id); | ||
385 | } else { | ||
386 | return this.rrd_header.getDSbyName(id); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | this.getNrRRAs = function() { | ||
391 | return this.rrd_header.getNrRRAs(); | ||
392 | } | ||
393 | |||
394 | this.getRRAInfo = function(idx) { | ||
395 | return this.rrd_header.getRRAInfo(idx); | ||
396 | } | ||
397 | |||
398 | this.getRRA = function(idx) { | ||
399 | var rra_info=this.rrd_header.getRRAInfo(idx); | ||
400 | return new RRDRRA(rrd_data, | ||
401 | this.rrd_header.rra_ptr_idx+idx*this.rrd_header.rra_ptr_el_size, | ||
402 | rra_info, | ||
403 | this.rrd_header.header_size, | ||
404 | this.rrd_header.rra_def_row_cnt_sums[idx], | ||
405 | this.rrd_header.ds_cnt); | ||
406 | } | ||
407 | |||
408 | } | ||
diff --git a/js/sprintf.js b/js/sprintf.js new file mode 100644 index 0000000..4d61fa2 --- /dev/null +++ b/js/sprintf.js | |||
@@ -0,0 +1,78 @@ | |||
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 | break; | ||
56 | case 'd': | ||
57 | return parseInt(value, 10); | ||
58 | break; | ||
59 | case 'e': | ||
60 | prefix = value < 0 ? '-' : ''; | ||
61 | return lpad(prefix+Math.abs(value).toExponential(precision),' ',width); | ||
62 | break; | ||
63 | case 'F': | ||
64 | case 'f': | ||
65 | prefix = value < 0 ? '-' : ''; | ||
66 | return lpad(prefix+Math.abs(value).toFixed(precision),' ',width); | ||
67 | break; | ||
68 | case 'g': | ||
69 | prefix = value < 0 ? '-' : ''; | ||
70 | return lpad(prefix+Math.abs(value).toPrecision(precision),' ',width); | ||
71 | break; | ||
72 | default: | ||
73 | return match; | ||
74 | } | ||
75 | |||
76 | }; | ||
77 | return fmt.replace(/%(\d+)?(\.(\d+))?(l?)([%scdfFeg])/g,format); | ||
78 | }; | ||
diff --git a/js/strftime.js b/js/strftime.js new file mode 100644 index 0000000..0933c27 --- /dev/null +++ b/js/strftime.js | |||
@@ -0,0 +1,146 @@ | |||
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 lpad (str, padString, length) | ||
41 | { | ||
42 | while (str.length < length) | ||
43 | str = padString + str; | ||
44 | return str; | ||
45 | }; | ||
46 | |||
47 | function format(match, opt) | ||
48 | { | ||
49 | if (match === '%%') return '%'; | ||
50 | |||
51 | switch (opt) { | ||
52 | case 'a': | ||
53 | return days[d.getDay()]; | ||
54 | break; | ||
55 | case 'A': | ||
56 | return fdays[d.getDay()]; | ||
57 | break; | ||
58 | case 'b': | ||
59 | return months[d.getMonth()]; | ||
60 | break; | ||
61 | case 'B': | ||
62 | return fmonths[d.getMonth()]; | ||
63 | break; | ||
64 | case 'c': | ||
65 | return d.toLocaleString(); | ||
66 | break; | ||
67 | case 'd': | ||
68 | return pad2(d.getDate()); | ||
69 | break; | ||
70 | case 'H': | ||
71 | return pad2(d.getHours()); | ||
72 | break; | ||
73 | case 'I': | ||
74 | var hours = d.getHours()%12; | ||
75 | return pad2(hours === 0 ? 12 : hours); | ||
76 | break; | ||
77 | case 'j': | ||
78 | var d01 = new Date (d.getFullYear(), 0, 1); | ||
79 | return pad3(Math.ceil((d.getTime()-d01.getTime())/86400000)+1); | ||
80 | break; | ||
81 | case 'm': | ||
82 | return pad2(d.getMonth()); | ||
83 | break; | ||
84 | case 'M': | ||
85 | return pad2(d.getMinutes()); | ||
86 | break; | ||
87 | case 'p': | ||
88 | return d.getHours() >= 12 ? 'PM' : 'AM'; | ||
89 | break; | ||
90 | case 's': | ||
91 | return pad2(d.getSeconds()); | ||
92 | break; | ||
93 | case 'S': | ||
94 | return d.getTime()/1000; | ||
95 | break; | ||
96 | case 'u': | ||
97 | return d.getDay() === 0 ? 7 : d.getDay(); | ||
98 | break; | ||
99 | case 'U': | ||
100 | var d01 = new Date(d.getFullYear(),0,1); | ||
101 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 6 - d.getDay())/7)); | ||
102 | break; | ||
103 | case 'V': | ||
104 | var d01 = new Date(d.getFullYear(), 0, 1); | ||
105 | var w = Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7); | ||
106 | var d31 = new Date(d.getFullYear(), 11, 31); | ||
107 | if (d01.getDay() < 4 && d01.getDay() > 1) w++; | ||
108 | if (w === 53 && d31.getDay() < 4) { | ||
109 | w = 1; | ||
110 | } else if (w === 0) { | ||
111 | d31 = new Date(d.getFullYear()-1, 11, 31); | ||
112 | d01 = new Date(d31.getFullYear(), 0, 1); | ||
113 | w = Math.round((Math.ceil((d31.getTime()-d01.getTime())/86400000)+1 + 7 - (d31.getDay() === 0 ? 7 : d31.getDay()))/7); | ||
114 | if (d01.getDay() < 4 && d01.getDay() > 1) w++; | ||
115 | if (w === 53 && d31.getDay() < 4) w = 1; | ||
116 | } | ||
117 | return pad2(w); | ||
118 | break; | ||
119 | case 'w': | ||
120 | return d.getDay(); | ||
121 | break; | ||
122 | case 'W': | ||
123 | var d01 = new Date(d.getFullYear(),0,1); | ||
124 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7)); | ||
125 | break; | ||
126 | case 'x': | ||
127 | return pad2(d.getDate())+'/'+pad2(d.getMonth())+'/'+d.getFullYear() | ||
128 | break; | ||
129 | case 'X': | ||
130 | return pad2(d.getHours())+':'+pad2(d.getMinutes())+':'+pad2(d.getSeconds()); | ||
131 | break; | ||
132 | case 'y': | ||
133 | return pad2(d.getFullYear()%100); | ||
134 | break; | ||
135 | case 'Y': | ||
136 | return d.getFullYear(); | ||
137 | break; | ||
138 | case 'Z': | ||
139 | return d.toString().replace(/^.*\(([^)]+)\)$/, '$1'); | ||
140 | break; | ||
141 | default: | ||
142 | return match; | ||
143 | } | ||
144 | }; | ||
145 | return fmt.replace(/%([aAbBcdHIjmMpsSUVwWxXyYZ%])/g, format); | ||
146 | }; | ||