aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/js/RrdGraph.js
diff options
context:
space:
mode:
authorPim van den Berg2013-05-10 21:36:54 +0200
committerPim van den Berg2013-05-10 21:36:54 +0200
commit0163faefef02207ad0ea3330af688103633293ff (patch)
treec407fca93a3418af59c4c7133327f5bc1630fba5 /js/RrdGraph.js
parentMerge pull request #5 from mce35/nut (diff)
downloadapt-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
Diffstat (limited to 'js/RrdGraph.js')
-rw-r--r--js/RrdGraph.js2914
1 files changed, 2914 insertions, 0 deletions
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 */
28var 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 */
39var 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
135RrdGraphDesc.GF_PRINT = 0;
136RrdGraphDesc.GF_GPRINT = 1;
137RrdGraphDesc.GF_COMMENT = 2;
138RrdGraphDesc.GF_HRULE = 3;
139RrdGraphDesc.GF_VRULE = 4;
140RrdGraphDesc.GF_LINE = 5;
141RrdGraphDesc.GF_AREA = 6;
142RrdGraphDesc.GF_STACK = 7;
143RrdGraphDesc.GF_TICK = 8;
144RrdGraphDesc.GF_TEXTALIGN = 9;
145RrdGraphDesc.GF_DEF = 10;
146RrdGraphDesc.GF_CDEF = 11;
147RrdGraphDesc.GF_VDEF = 12;
148RrdGraphDesc.GF_SHIFT = 13;
149RrdGraphDesc.GF_XPORT = 14;
150
151RrdGraphDesc.CF_AVERAGE = 0;
152RrdGraphDesc.CF_MINIMUM = 1;
153RrdGraphDesc.CF_MAXIMUM = 2;
154RrdGraphDesc.CF_LAST = 3;
155RrdGraphDesc.CF_HWPREDICT = 4;
156RrdGraphDesc.CF_SEASONAL = 5;
157RrdGraphDesc.CF_DEVPREDICT = 6;
158RrdGraphDesc.CF_DEVSEASONAL = 7;
159RrdGraphDesc.CF_FAILURES = 8;
160RrdGraphDesc.CF_MHWPREDICT = 9;
161
162RrdGraphDesc.TXA_LEFT = 0;
163RrdGraphDesc.TXA_RIGHT = 1;
164RrdGraphDesc.TXA_CENTER = 2;
165RrdGraphDesc.TXA_JUSTIFIED = 3;
166
167RrdGraphDesc.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
184RrdGraphDesc.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
201RrdGraphDesc.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
232RrdGraphDesc.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
241RrdGraphDesc.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
256RrdGraphDesc.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
280RrdGraphDesc.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
294RrdGraphDesc.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
307RrdGraphDesc.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
320RrdGraphDesc.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
346RrdGraphDesc.prototype.comment = function (graph, text)
347{
348 this.gf = RrdGraphDesc.GF_COMMENT;
349 this.vidx = -1;
350 this.legend = text;
351};
352
353RrdGraphDesc.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
370RrdGraphDesc.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
379RrdGraphDesc.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 */
392var 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 */
403var 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
475RrdVdef.VDEF_MAXIMUM = 0;
476RrdVdef.VDEF_MINIMUM = 1;
477RrdVdef.VDEF_AVERAGE = 2;
478RrdVdef.VDEF_STDEV = 3;
479RrdVdef.VDEF_PERCENT = 4;
480RrdVdef.VDEF_TOTAL = 5;
481RrdVdef.VDEF_FIRST = 6;
482RrdVdef.VDEF_LAST = 7;
483RrdVdef.VDEF_LSLSLOPE = 8;
484RrdVdef.VDEF_LSLINT = 9;
485RrdVdef.VDEF_LSLCORREL = 10;
486RrdVdef.VDEF_PERCENTNAN = 11;
487
488RrdVdef.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
507RrdVdef.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 */
693var 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 */
704var 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
888RrdGraph.TMT_SECOND = 0;
889RrdGraph.TMT_MINUTE = 1;
890RrdGraph.TMT_HOUR = 2;
891RrdGraph.TMT_DAY = 3;
892RrdGraph.TMT_WEEK = 4;
893RrdGraph.TMT_MONTH = 5;
894RrdGraph.TMT_YEAR = 6;
895
896RrdGraph.GFX_H_LEFT = 1;
897RrdGraph.GFX_H_RIGHT = 2;
898RrdGraph.GFX_H_CENTER = 3;
899
900RrdGraph.GFX_V_TOP = 1;
901RrdGraph.GFX_V_BOTTOM = 2;
902RrdGraph.GFX_V_CENTER = 3;
903
904RrdGraph.LEGEND_POS_NORTH = 0;
905RrdGraph.LEGEND_POS_WEST = 1;
906RrdGraph.LEGEND_POS_SOUTH = 2;
907RrdGraph.LEGEND_POS_EAST = 3;
908
909RrdGraph.LEGEND_DIR_TOP_DOWN = 0;
910RrdGraph.LEGEND_DIR_BOTTOM_UP = 1;
911
912RrdGraph.prototype.set_default_font = function (name)
913{
914 for (var font in this.TEXT)
915 this.TEXT[font].font = name;
916};
917
918RrdGraph.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
936RrdGraph.prototype.color2rgba = function (color)
937{
938 return 'rgba('+color[0]+','+color[1]+','+color[2]+','+color[3]+')';
939};
940
941RrdGraph.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
950RrdGraph.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
972RrdGraph.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
991RrdGraph.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
1005RrdGraph.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
1065RrdGraph.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
1107RrdGraph.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
1222RrdGraph.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
1321RrdGraph.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
1377RrdGraph.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
1391RrdGraph.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
1482RrdGraph.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
1577RrdGraph.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
1718RrdGraph.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
1752RrdGraph.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
1766RrdGraph.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
1944RrdGraph.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
2010RrdGraph.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
2030RrdGraph.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
2054RrdGraph.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
2098RrdGraph.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
2164RrdGraph.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
2244RrdGraph.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
2349RrdGraph.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
2544RrdGraph.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
2828RrdGraph.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
2841RrdGraph.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
2846RrdGraph.prototype.gdes_add_cdef = function (vname, rpn)
2847{
2848 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_CDEF, vname, rpn));
2849};
2850
2851RrdGraph.prototype.gdes_add_vdef = function (vname, rpn)
2852{
2853 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VDEF, vname, rpn));
2854};
2855
2856RrdGraph.prototype.gdes_add_shift = function (vname, offset)
2857{
2858 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_SHIFT, vname, offset));
2859};
2860
2861RrdGraph.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
2866RrdGraph.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
2871RrdGraph.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
2876RrdGraph.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
2881RrdGraph.prototype.gdes_add_comment = function (text)
2882{
2883 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_COMMENT, text));
2884};
2885
2886RrdGraph.prototype.gdes_add_textalign = function (align)
2887{
2888 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_TEXTALING, align));
2889};
2890
2891RrdGraph.prototype.gdes_add_vrule = function (time, color, legend)
2892{
2893 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VRULE, time, color, legend));
2894};
2895
2896RrdGraph.prototype.gdes_add_hrule = function (value, color, legend)
2897{
2898 this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_HRULE, value, color, legend));
2899};
2900
2901RrdGraph.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