diff options
| -rw-r--r-- | js/Color.js | 76 | ||||
| -rw-r--r-- | js/RrdCmdLine.js | 513 | ||||
| -rw-r--r-- | js/RrdDataFile.js | 129 | ||||
| -rw-r--r-- | js/RrdGfxCanvas.js | 254 | ||||
| -rw-r--r-- | js/RrdGfxPdf.js | 1014 | ||||
| -rw-r--r-- | js/RrdGfxSvg.js | 250 | ||||
| -rw-r--r-- | js/RrdGraph.js | 2914 | ||||
| -rw-r--r-- | js/RrdJson.js | 588 | ||||
| -rw-r--r-- | js/RrdRpn.js | 616 | ||||
| -rw-r--r-- | js/RrdTime.js | 621 | ||||
| -rw-r--r-- | js/base64.js | 143 | ||||
| -rw-r--r-- | js/binaryXHR.js | 234 | ||||
| -rw-r--r-- | js/rrdFile.js | 408 | ||||
| -rw-r--r-- | js/sprintf.js | 78 | ||||
| -rw-r--r-- | js/strftime.js | 146 |
15 files changed, 7984 insertions, 0 deletions
diff --git a/js/Color.js b/js/Color.js new file mode 100644 index 0000000..e294f65 --- /dev/null +++ b/js/Color.js | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | * | ||
| 17 | **/ | ||
| 18 | |||
| 19 | "use strict"; | ||
| 20 | |||
| 21 | /** | ||
| 22 | * ColorError | ||
| 23 | * @constructor | ||
| 24 | */ | ||
| 25 | var ColorError = function (message) | ||
| 26 | { | ||
| 27 | this.prototype = Error.prototype; | ||
| 28 | this.name = "ColorError"; | ||
| 29 | this.message = (message) ? message : "Error"; | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Color | ||
| 34 | * @constructor | ||
| 35 | */ | ||
| 36 | function Color(str) | ||
| 37 | { | ||
| 38 | var bits; | ||
| 39 | |||
| 40 | this.r = 0; | ||
| 41 | this.g = 0; | ||
| 42 | this.b = 0; | ||
| 43 | this.a = 1.0; | ||
| 44 | |||
| 45 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) { | ||
| 46 | this.r = parseInt(bits[1]+bits[1], 16); | ||
| 47 | this.g = parseInt(bits[2]+bits[2], 16); | ||
| 48 | this.b = parseInt(bits[3]+bits[3], 16); | ||
| 49 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 50 | this.r = parseInt(bits[1], 16); | ||
| 51 | this.g = parseInt(bits[2], 16); | ||
| 52 | this.b = parseInt(bits[3], 16); | ||
| 53 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 54 | this.r = parseInt(bits[1], 16); | ||
| 55 | this.g = parseInt(bits[2], 16); | ||
| 56 | this.b = parseInt(bits[3], 16); | ||
| 57 | this.a = parseInt(bits[4], 16)/255; | ||
| 58 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) { | ||
| 59 | this.r = parseInt(bits[1], 10); | ||
| 60 | this.g = parseInt(bits[2], 10); | ||
| 61 | this.b = parseInt(bits[3], 10); | ||
| 62 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) { | ||
| 63 | this.r = parseInt(bits[1], 10); | ||
| 64 | this.g = parseInt(bits[2], 10); | ||
| 65 | this.b = parseInt(bits[3], 10); | ||
| 66 | this.a = parseFloat(bits[4], 10); | ||
| 67 | } else { | ||
| 68 | throw new ColorError("Unknow color format '"+str+"'"); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | |||
| 72 | Color.prototype.torgba = function () | ||
| 73 | { | ||
| 74 | return 'rgba('+this.r+','+this.g+','+this.b+','+this.a+')'; | ||
| 75 | }; | ||
| 76 | |||
diff --git a/js/RrdCmdLine.js b/js/RrdCmdLine.js new file mode 100644 index 0000000..fea0753 --- /dev/null +++ b/js/RrdCmdLine.js | |||
| @@ -0,0 +1,513 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * | ||
| 18 | * Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 19 | **/ | ||
| 20 | |||
| 21 | "use strict"; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * RrdCmdLine | ||
| 25 | * @constructor | ||
| 26 | */ | ||
| 27 | var RrdCmdLine = function() { | ||
| 28 | // if (arguments.lenght === 3) // XXX | ||
| 29 | this.init.apply(this, arguments); | ||
| 30 | }; | ||
| 31 | |||
| 32 | RrdCmdLine.prototype = { | ||
| 33 | graph: null, | ||
| 34 | |||
| 35 | init: function (gfx, fetch, line) | ||
| 36 | { | ||
| 37 | this.graph = new RrdGraph(gfx, fetch); | ||
| 38 | this.cmdline(line); | ||
| 39 | }, | ||
| 40 | cmdline: function(line) // FIXME | ||
| 41 | { | ||
| 42 | var i = 0; | ||
| 43 | line = line.replace(/\n/g," "); | ||
| 44 | var lines = line.match(/[^" ]+|"[^"]+"/g); | ||
| 45 | var len = lines.length; | ||
| 46 | |||
| 47 | while (i < len) { | ||
| 48 | var arg = lines[i]; | ||
| 49 | if (arg.charAt(0) === '"' && arg.charAt(arg.length-1) === '"') | ||
| 50 | arg = arg.substr(1,arg.length-2); | ||
| 51 | if (/^LINE[0-9.]+:/.test(arg)) { | ||
| 52 | this.parse_line(arg); | ||
| 53 | } else if (/^AREA:/.test(arg)) { | ||
| 54 | this.parse_area(arg); | ||
| 55 | } else if (/^DEF:/.test(arg)) { | ||
| 56 | this.parse_def(arg); | ||
| 57 | } else if (/^CDEF:/.test(arg)) { | ||
| 58 | this.parse_cdef(arg); | ||
| 59 | } else if (/^VDEF:/.test(arg)) { | ||
| 60 | this.parse_vdef(arg); | ||
| 61 | } else if (/^GPRINT:/.test(arg)) { | ||
| 62 | this.parse_gprint(arg); | ||
| 63 | } else if (/^COMMENT:/.test(arg)) { | ||
| 64 | this.parse_comment(arg); | ||
| 65 | } else if (/^VRULE:/.test(arg)) { | ||
| 66 | this.parse_vrule(arg); | ||
| 67 | } else if (/^HRULE:/.test(arg)) { | ||
| 68 | this.parse_hrule(arg); | ||
| 69 | } else if (/^TICK:/.test(arg)) { | ||
| 70 | this.parse_tick(arg); | ||
| 71 | } else if (/^TEXTALIGN:/.test(arg)) { | ||
| 72 | this.parse_textaling(arg); | ||
| 73 | } else if (/^SHIFT:/.test(arg)) { | ||
| 74 | this.parse_shift(arg); | ||
| 75 | } else if (arg.charAt(0) === '-') { | ||
| 76 | var strip = 1; | ||
| 77 | if (arg.length > 1 && arg.charAt(1) === '-') { | ||
| 78 | strip = 2; | ||
| 79 | } | ||
| 80 | var option = arg.substr(strip); | ||
| 81 | var value = undefined; | ||
| 82 | |||
| 83 | if (option.indexOf('=') !== -1) { | ||
| 84 | var index = option.indexOf('='); | ||
| 85 | value = option.substr(index+1); | ||
| 86 | option = option.substr(0,index); | ||
| 87 | } else if (i+1 < len) { | ||
| 88 | if (lines[i+1].charAt(0) !== '-' && | ||
| 89 | !/^"?LINE[0-9.]+:/.test(lines[i+1]) && | ||
| 90 | !/^"?AREA:/.test(lines[i+1]) && | ||
| 91 | !/^"?DEF:/.test(lines[i+1]) && | ||
| 92 | !/^"?CDEF:/.test(lines[i+1]) && | ||
| 93 | !/^"?VDEF:/.test(lines[i+1]) && | ||
| 94 | !/^"?GPRINT:/.test(lines[i+1]) && | ||
| 95 | !/^"?COMMENT:/.test(lines[i+1]) && | ||
| 96 | !/^"?HRULE:/.test(lines[i+1]) && | ||
| 97 | !/^"?VRULE:/.test(lines[i+1]) && | ||
| 98 | !/^"?TICK:/.test(lines[i+1]) && | ||
| 99 | !/^"?TEXTALING:/.test(lines[i+1]) && | ||
| 100 | !/^"?SHIFT:/.test(lines[i+1]) | ||
| 101 | ) { | ||
| 102 | i++; | ||
| 103 | if (lines[i].charAt(0) === '"' && lines[i].charAt(lines[i].length-1) === '"') | ||
| 104 | value = lines[i].substr(1,lines[i].length-2); | ||
| 105 | else | ||
| 106 | value = lines[i]; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | this.set_option(option, value); | ||
| 110 | } else { | ||
| 111 | throw "Unknow argument: "+arg; | ||
| 112 | } | ||
| 113 | i++; | ||
| 114 | } | ||
| 115 | var start_end = RrdTime.proc_start_end(this.graph.start_t, this.graph.end_t); // FIXME here? | ||
| 116 | this.graph.start = start_end[0]; | ||
| 117 | this.graph.end = start_end[1]; | ||
| 118 | }, | ||
| 119 | set_option: function(option, value) | ||
| 120 | { | ||
| 121 | switch(option) { | ||
| 122 | case 'alt-autoscale': | ||
| 123 | case 'A': | ||
| 124 | this.graph.alt_autoscale = true; | ||
| 125 | break; | ||
| 126 | case 'base': | ||
| 127 | case 'b': | ||
| 128 | this.graph.base = parseInt(value, 10); | ||
| 129 | if (this.graph.base !== 1000 && this.graph.base !== 1024) | ||
| 130 | throw 'the only sensible value for base apart from 1000 is 1024'; | ||
| 131 | break; | ||
| 132 | case 'color': | ||
| 133 | case 'c': | ||
| 134 | var index = value.indexOf('#'); | ||
| 135 | if (index === -1) | ||
| 136 | throw "invalid color def format"; | ||
| 137 | var name = value.substr(0,index); | ||
| 138 | if (!this.graph.GRC[name]) | ||
| 139 | throw "invalid color name '"+name+"'" | ||
| 140 | this.graph.GRC[name] = value.substr(index); // FIXME check color | ||
| 141 | break; | ||
| 142 | case 'full-size-mode': | ||
| 143 | case 'D': | ||
| 144 | this.graph.full_size_mode = true; | ||
| 145 | break; | ||
| 146 | case 'slope-mode': | ||
| 147 | case 'E': | ||
| 148 | this.graph.slopemode = true; | ||
| 149 | break; | ||
| 150 | case 'end': | ||
| 151 | case 'e': | ||
| 152 | this.graph.end_t = new RrdTime(value); | ||
| 153 | // this.graph.end = parseInt(value, 10); | ||
| 154 | break; | ||
| 155 | case 'force-rules-legend': | ||
| 156 | case 'F': | ||
| 157 | this.graph.force_rules_legend = true; | ||
| 158 | break; | ||
| 159 | case 'imginfo': | ||
| 160 | case 'f': | ||
| 161 | // im->imginfo = optarg; | ||
| 162 | break; | ||
| 163 | case 'graph-render-mode': | ||
| 164 | case 'G': | ||
| 165 | // im->graph_antialias | ||
| 166 | break; | ||
| 167 | case 'no-legend': | ||
| 168 | case 'g': | ||
| 169 | this.graph.no_legend = true; | ||
| 170 | break; | ||
| 171 | case 'height': | ||
| 172 | case 'h': | ||
| 173 | this.graph.ysize = parseInt(value, 10); | ||
| 174 | break; | ||
| 175 | case 'no-minor': | ||
| 176 | case 'I': | ||
| 177 | this.graph.no_minor = false; | ||
| 178 | break; | ||
| 179 | case 'interlaced': | ||
| 180 | case 'i': | ||
| 181 | break; | ||
| 182 | case 'alt-autoscale-min': | ||
| 183 | case 'J': | ||
| 184 | this.graph.alt_autoscale_min = true; | ||
| 185 | break; | ||
| 186 | case 'only-graph': | ||
| 187 | case 'j': | ||
| 188 | this.graph.only_graph = true; | ||
| 189 | break; | ||
| 190 | case 'units-length': | ||
| 191 | case 'L': | ||
| 192 | this.graph.unitslength = parseInt(value, 10); | ||
| 193 | this.graph.forceleftspace = true; | ||
| 194 | break; | ||
| 195 | case 'lower-limit': | ||
| 196 | case 'l': | ||
| 197 | this.graph.setminval = parseFloat(value) | ||
| 198 | break; | ||
| 199 | case 'alt-autoscale-max': | ||
| 200 | case 'M': | ||
| 201 | this.graph.alt_autoscale_max = true; | ||
| 202 | break; | ||
| 203 | case 'zoom': | ||
| 204 | case 'm': | ||
| 205 | this.graph.zoom = parseFloat(value); | ||
| 206 | if (this.graph.zoom <= 0.0) | ||
| 207 | throw "zoom factor must be > 0"; | ||
| 208 | break; | ||
| 209 | case 'no-gridfit': | ||
| 210 | case 'N': | ||
| 211 | this.graph.gridfit = true; | ||
| 212 | break; | ||
| 213 | case 'font': | ||
| 214 | case 'n': | ||
| 215 | var args = value.split(':'); | ||
| 216 | if (args.length !== 3) | ||
| 217 | throw "invalid text property format"; | ||
| 218 | if (!this.graph.TEXT[args[0]]) | ||
| 219 | throw "invalid fonttag '"+args[0]+"'" | ||
| 220 | if (args[1] > 0) | ||
| 221 | this.graph.TEXT[args[0]].size = args[1]; | ||
| 222 | if (args[2]) | ||
| 223 | this.graph.TEXT[args[0]].font = args[2]; | ||
| 224 | break; | ||
| 225 | case 'logarithmic': | ||
| 226 | case 'o': | ||
| 227 | this.graph.logarithmic = true; | ||
| 228 | break; | ||
| 229 | case 'pango-markup': | ||
| 230 | case 'P': | ||
| 231 | // im->with_markup = 1; | ||
| 232 | break; | ||
| 233 | case 'font-render-mode': | ||
| 234 | case 'R': | ||
| 235 | // im->font_options: normal light mono | ||
| 236 | break; | ||
| 237 | case 'rigid': | ||
| 238 | case 'r': | ||
| 239 | this.graph.rigid = true; | ||
| 240 | break; | ||
| 241 | case 'step': | ||
| 242 | this.graph.step = parseInt(value, 10); | ||
| 243 | break; | ||
| 244 | case 'start': | ||
| 245 | case 's': | ||
| 246 | this.graph.start_t = new RrdTime(value); | ||
| 247 | //this.graph.start = parseInt(value, 10); | ||
| 248 | break; | ||
| 249 | case 'tabwidth': | ||
| 250 | case 'T': | ||
| 251 | this.graph.tabwidth = parseFloat(value); | ||
| 252 | break; | ||
| 253 | case 'title': | ||
| 254 | case 't': | ||
| 255 | this.graph.title = value; | ||
| 256 | break; | ||
| 257 | case 'upper-limit': | ||
| 258 | case 'u': | ||
| 259 | this.graph.setmaxval = parseFloat(value); | ||
| 260 | break; | ||
| 261 | case 'vertical-label': | ||
| 262 | case 'v': | ||
| 263 | this.graph.ylegend = value; | ||
| 264 | break; | ||
| 265 | case 'watermark': | ||
| 266 | case 'W': | ||
| 267 | this.graph.watermark = value; | ||
| 268 | break; | ||
| 269 | case 'width': | ||
| 270 | case 'w': | ||
| 271 | this.graph.xsize = parseInt(value, 10); | ||
| 272 | if (this.graph.xsize < 10) | ||
| 273 | throw "width below 10 pixels"; | ||
| 274 | break; | ||
| 275 | case 'units-exponent': | ||
| 276 | case 'X': | ||
| 277 | this.graph.unitsexponent = parseInt(value, 10); | ||
| 278 | break; | ||
| 279 | case 'x-grid': | ||
| 280 | case 'x': | ||
| 281 | if (value === 'none') { | ||
| 282 | this.graph.draw_x_grid = false; | ||
| 283 | } else { | ||
| 284 | var args = value.split(':'); | ||
| 285 | if (args.length !== 8) | ||
| 286 | throw "invalid x-grid format"; | ||
| 287 | this.graph.xlab_user.gridtm = this.graph.tmt_conv(args[0]); | ||
| 288 | if (this.graph.xlab_user.gridtm < 0) | ||
| 289 | throw "unknown keyword "+args[0]; | ||
| 290 | this.graph.xlab_user.gridst = parseInt(args[1], 10); | ||
| 291 | this.graph.xlab_user.mgridtm = this.graph.tmt_conv(args[2]); | ||
| 292 | if (this.graph.xlab_user.mgridtm < 2) | ||
| 293 | throw "unknown keyword "+args[2]; | ||
| 294 | this.graph.xlab_user.mgridst = parseInt(args[3], 10); | ||
| 295 | this.graph.xlab_user.labtm = this.graph.tmt_conv(args[4]); | ||
| 296 | if (this.graph.xlab_user.labtm < 0) | ||
| 297 | throw "unknown keyword "+args[4]; | ||
| 298 | this.graph.xlab_user.labst = parseInt(args[5], 10); | ||
| 299 | this.graph.xlab_user.precis = parseInt(args[6], 10); | ||
| 300 | this.graph.xlab_user.minsec = 1; | ||
| 301 | this.graph.xlab_form = args[7]; // FIXME : ? join(:) | ||
| 302 | this.graph.xlab_user.stst = this.graph.xlab_form; | ||
| 303 | } | ||
| 304 | break; | ||
| 305 | case 'alt-y-grid': | ||
| 306 | case 'Y': | ||
| 307 | this.graph.alt_ygrid = true; | ||
| 308 | break; | ||
| 309 | case 'y-grid': | ||
| 310 | case 'y': | ||
| 311 | if (value === 'none') { | ||
| 312 | this.graph.draw_y_grid = false; | ||
| 313 | } else { | ||
| 314 | var index = value.indexOf(':'); | ||
| 315 | if (index === -1) | ||
| 316 | throw "invalid y-grid format"; | ||
| 317 | this.graph.ygridstep = parseFloat(value.substr(0,index)); | ||
| 318 | if (this.graph.ygridstep <= 0) | ||
| 319 | throw "grid step must be > 0"; | ||
| 320 | this.graph.ylabfact = parseInt(value.substr(index+1), 10); | ||
| 321 | if (this.graph.ylabfact < 1) | ||
| 322 | throw "label factor must be > 0"; | ||
| 323 | } | ||
| 324 | break; | ||
| 325 | case 'lazy': | ||
| 326 | case 'z': | ||
| 327 | this.graph.lazy = 1; | ||
| 328 | break; | ||
| 329 | case 'units': | ||
| 330 | if (this.graph.force_units) | ||
| 331 | throw "--units can only be used once!"; | ||
| 332 | if (value === 'si') | ||
| 333 | this.graph.force_units_si = true; | ||
| 334 | else | ||
| 335 | throw "invalid argument for --units: "+value; | ||
| 336 | break; | ||
| 337 | case 'alt-y-mrtg': | ||
| 338 | break; | ||
| 339 | case 'disable-rrdtool-tag': | ||
| 340 | this.graph.no_rrdtool_tag = true; | ||
| 341 | break; | ||
| 342 | case 'right-axis': | ||
| 343 | var index = value.indexOf(':'); | ||
| 344 | if (index === -1) | ||
| 345 | throw "invalid right-axis format expected scale:shift"; | ||
| 346 | this.graph.second_axis_scale = parseFloat(value.substr(0,index)); | ||
| 347 | if(this.graph.second_axis_scale === 0) | ||
| 348 | throw "the second_axis_scale must not be 0"; | ||
| 349 | this.graph.second_axis_shift = parseFloat(value.substr(index+1)); | ||
| 350 | break; | ||
| 351 | case 'right-axis-label': | ||
| 352 | this.graph.second_axis_legend = value; | ||
| 353 | break; | ||
| 354 | case 'right-axis-format': | ||
| 355 | this.graph.second_axis_format = value; | ||
| 356 | break; | ||
| 357 | case 'legend-position': | ||
| 358 | if (value === "north") { | ||
| 359 | this.graph.legendposition = this.graph.LEGEND_POS.NORTH; | ||
| 360 | } else if (value === "west") { | ||
| 361 | this.graph.legendposition = this.graph.LEGEND_POS.WEST; | ||
| 362 | } else if (value === "south") { | ||
| 363 | this.graph.legendposition = this.graph.LEGEND_POS.SOUTH; | ||
| 364 | } else if (value === "east") { | ||
| 365 | this.graph.legendposition = this.graph.LEGEND_POS.EAST; | ||
| 366 | } else { | ||
| 367 | throw "unknown legend-position '"+value+"'"; | ||
| 368 | } | ||
| 369 | break; | ||
| 370 | case 'legend-direction': | ||
| 371 | if (value === "topdown") { | ||
| 372 | this.graph.legenddirection = this.graph.LEGEND_DIR.TOP_DOWN; | ||
| 373 | } else if (value === "bottomup") { | ||
| 374 | this.graph.legenddirection = this.graph.LEGEND_DIR.BOTTOM_UP; | ||
| 375 | } else { | ||
| 376 | throw "unknown legend-position '"+value+"'"; | ||
| 377 | } | ||
| 378 | break; | ||
| 379 | case 'border': | ||
| 380 | this.graph.draw_3d_border = parseInt(value, 10); | ||
| 381 | break; | ||
| 382 | case 'grid-dash': | ||
| 383 | var index = value.indexOf(':'); | ||
| 384 | if (index === -1) | ||
| 385 | throw "expected grid-dash format float:float"; | ||
| 386 | this.graph.grid_dash_on = parseFloat(value.substr(0,index)); | ||
| 387 | this.graph.grid_dash_off = parseFloat(value.substr(index+1)); | ||
| 388 | break; | ||
| 389 | case 'dynamic-labels': | ||
| 390 | this.graph.dynamic_labels = true; | ||
| 391 | break; | ||
| 392 | default: | ||
| 393 | throw 'Unknow option "'+option+'"'; | ||
| 394 | } | ||
| 395 | |||
| 396 | }, | ||
| 397 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
| 398 | parse_def: function (line) | ||
| 399 | { | ||
| 400 | var args = line.split(/:/); | ||
| 401 | var n=1; | ||
| 402 | var vnames = args[n++].split('='); | ||
| 403 | var vname = vnames[0]; | ||
| 404 | var rrdfile = vnames[1]; | ||
| 405 | var name = args[n++]; | ||
| 406 | var cf = args[n++]; | ||
| 407 | var step = undefined; | ||
| 408 | var reduce = undefined; | ||
| 409 | var start = undefined; | ||
| 410 | var end = undefined; | ||
| 411 | if (args.length > n) { | ||
| 412 | for (var j = n, xlen = args.length ; j < xlen ; j++) { | ||
| 413 | var opts = args[j].split("="); | ||
| 414 | if (opts[0] === "step") step = opts[1]; | ||
| 415 | if (opts[0] === "reduce") reduce = opts[1] | ||
| 416 | if (opts[0] === "start") start = opts[1]; | ||
| 417 | if (opts[0] === "end") end = opts[1]; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | this.graph.gdes_add_def(vname, rrdfile, name, cf, step, start, end, reduce) | ||
| 421 | }, | ||
| 422 | // CDEF:vname=RPN expression | ||
| 423 | parse_cdef: function (line) | ||
| 424 | { | ||
| 425 | var args = line.split(/:|=/); | ||
| 426 | this.graph.gdes_add_cdef(args[1], args[2]); | ||
| 427 | }, | ||
| 428 | // VDEF:vname=RPN expression | ||
| 429 | parse_vdef: function (line) | ||
| 430 | { | ||
| 431 | var args = line.split(/:|=/); | ||
| 432 | this.graph.gdes_add_vdef(args[1], args[2]); | ||
| 433 | }, | ||
| 434 | // SHIFT:vname:offset | ||
| 435 | parse_shift: function (line) | ||
| 436 | { | ||
| 437 | var args = line.split(':'); | ||
| 438 | this.graph.gdes_add_shift(args[1], args[2]); | ||
| 439 | }, | ||
| 440 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 441 | parse_line: function (line) | ||
| 442 | { | ||
| 443 | var args = line.split(/#|:/); | ||
| 444 | var width = parseFloat(args[0].substr(4)); | ||
| 445 | var stack = args[4] === 'STACK' ? true : undefined; | ||
| 446 | var color = this.graph.parse_color(args[2]); | ||
| 447 | this.graph.gdes_add_line(width, args[1], this.graph.color2rgba(color), args[3], stack); | ||
| 448 | }, | ||
| 449 | // AREA:value[#color][:[legend][:STACK]] | ||
| 450 | parse_area: function (line) | ||
| 451 | { | ||
| 452 | var args = line.split(/#|:/); | ||
| 453 | var stack = args[3] === 'STACK' ? true : undefined; | ||
| 454 | var color = this.graph.parse_color(args[2]); | ||
| 455 | this.graph.gdes_add_area(args[1], this.graph.color2rgba(color), stack); | ||
| 456 | }, | ||
| 457 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
| 458 | parse_tick: function (line) | ||
| 459 | { | ||
| 460 | var args = line.split(/:|#/); | ||
| 461 | var color = this.graph.parse_color(args[2]); | ||
| 462 | this.graph.gdes_add_tick(args[1], this.graph.color2rgba(color), args[3], args[4]); | ||
| 463 | }, | ||
| 464 | // GPRINT:vname:format | ||
| 465 | parse_gprint: function(line) | ||
| 466 | { | ||
| 467 | var args = line.split(':'); | ||
| 468 | var strftime = false; | ||
| 469 | var vname = args[1]; | ||
| 470 | var cf = args[2]; | ||
| 471 | var format = ""; | ||
| 472 | if (args.length > 3) { | ||
| 473 | var m=0; | ||
| 474 | for (var j = 3, xlen = args.length ; j < xlen ; j++) { | ||
| 475 | if (args[j] === 'strftime') { | ||
| 476 | strftime = true; | ||
| 477 | } else { | ||
| 478 | if (m>0) { | ||
| 479 | format = format + ':'+ args[j]; | ||
| 480 | } else { | ||
| 481 | format = args[j]; | ||
| 482 | } | ||
| 483 | m++; | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | this.graph.gdes_add_gprint(vname, cf, format, strftime); | ||
| 488 | }, | ||
| 489 | //COMMENT:text | ||
| 490 | parse_comment: function (line) | ||
| 491 | { | ||
| 492 | var index = line.indexOf(':'); | ||
| 493 | this.graph.gdes_add_comment(line.substr(index+1)); | ||
| 494 | }, | ||
| 495 | // TEXTALIGN:{left|right|justified|center} | ||
| 496 | parse_textaling: function (line) | ||
| 497 | { | ||
| 498 | var index = line.indexOf(':'); | ||
| 499 | this.graph.gdes_add_textaling(line.substr(index+1)); | ||
| 500 | }, | ||
| 501 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 502 | parse_vrule: function (line) | ||
| 503 | { | ||
| 504 | var args = line.split(/:|#/); | ||
| 505 | this.graph.gdes_add_vrule(args[1], '#'+args[2], args[3]); | ||
| 506 | }, | ||
| 507 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 508 | parse_hrule: function (line) | ||
| 509 | { | ||
| 510 | var args = line.split(/:|#/); | ||
| 511 | this.graph.gdes_add_hrule(args[1], '#'+args[2], args[3]); | ||
| 512 | } | ||
| 513 | }; | ||
diff --git a/js/RrdDataFile.js b/js/RrdDataFile.js new file mode 100644 index 0000000..ae4dcff --- /dev/null +++ b/js/RrdDataFile.js | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * | ||
| 18 | * Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 19 | **/ | ||
| 20 | |||
| 21 | "use strict"; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * RrdDataFile | ||
| 25 | * @constructor | ||
| 26 | */ | ||
| 27 | var RrdDataFile = function() { | ||
| 28 | this.init.apply(this, arguments); | ||
| 29 | }; | ||
| 30 | |||
| 31 | RrdDataFile.prototype = { | ||
| 32 | rrdfiles: null, | ||
| 33 | |||
| 34 | init: function() | ||
| 35 | { | ||
| 36 | this.rrdfiles = {}; | ||
| 37 | }, | ||
| 38 | fetch: function(gdp, ft_step) | ||
| 39 | { | ||
| 40 | var cal_start, cal_end; | ||
| 41 | var best_full_rra = 0, best_part_rra = 0, chosen_rra = 0; | ||
| 42 | var best_full_step_diff = 0, best_part_step_diff = 0, tmp_step_diff = 0, tmp_match = 0, best_match = 0; | ||
| 43 | var full_match, rra_base; | ||
| 44 | var first_full = 1; | ||
| 45 | var first_part = 1; | ||
| 46 | var rrd; | ||
| 47 | var data_ptr; | ||
| 48 | var rows; | ||
| 49 | |||
| 50 | if (gdp.rrd in this.rrdfiles) { | ||
| 51 | rrd = this.rrdfiles[gdp.rrd]; | ||
| 52 | } else { | ||
| 53 | var bf = FetchBinaryURL(gdp.rrd); | ||
| 54 | rrd = new RRDFile(bf); | ||
| 55 | this.rrdfiles[gdp.rrd] = rrd; | ||
| 56 | } | ||
| 57 | |||
| 58 | var cf_idx = gdp.cf; | ||
| 59 | var ds_cnt = rrd.getNrDSs(); | ||
| 60 | var rra_cnt = rrd.getNrRRAs(); | ||
| 61 | |||
| 62 | for (var i = 0; i < ds_cnt; i++) | ||
| 63 | gdp.ds_namv[i] = rrd.rrd_header.getDSbyIdx(i).getName(); | ||
| 64 | |||
| 65 | for (var i = 0; i < rra_cnt; i++) { | ||
| 66 | var rra = rrd.getRRAInfo(i); | ||
| 67 | if (RrdGraphDesc.cf_conv(rra.getCFName()) === cf_idx) { | ||
| 68 | cal_end = (rrd.getLastUpdate() - (rrd.getLastUpdate() % (rra.getPdpPerRow() * rra.pdp_step))); | ||
| 69 | cal_start = (cal_end - (rra.getPdpPerRow() * rra.row_cnt * rra.pdp_step)); | ||
| 70 | full_match = gdp.end - gdp.start; | ||
| 71 | |||
| 72 | tmp_step_diff = Math.abs(ft_step - (rrd.getMinStep() * rra.pdp_cnt)); | ||
| 73 | if (cal_start <= gdp.start) { | ||
| 74 | if (first_full || (tmp_step_diff < best_full_step_diff)) { | ||
| 75 | first_full = 0; | ||
| 76 | best_full_step_diff = tmp_step_diff; | ||
| 77 | best_full_rra = i; | ||
| 78 | } | ||
| 79 | } else { | ||
| 80 | tmp_match = full_match; | ||
| 81 | if (cal_start > gdp.start) tmp_match -= (cal_start - gdp.start); | ||
| 82 | if (first_part || (best_match < tmp_match) || (best_match === tmp_match && tmp_step_diff < best_part_step_diff)) { | ||
| 83 | first_part = 0; | ||
| 84 | best_match = tmp_match; | ||
| 85 | best_part_step_diff = tmp_step_diff; | ||
| 86 | best_part_rra = i; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | if (first_full === 0) chosen_rra = best_full_rra; | ||
| 93 | else if (first_part === 0) chosen_rra = best_part_rra; | ||
| 94 | else throw "the RRD does not contain an RRA matching the chosen CF"; | ||
| 95 | |||
| 96 | var rra_info = rrd.getRRAInfo(chosen_rra); | ||
| 97 | var rra = rrd.getRRA(chosen_rra); | ||
| 98 | |||
| 99 | ft_step = rrd.rrd_header.pdp_step * rra_info.getPdpPerRow(); | ||
| 100 | gdp.start -= (gdp.start % ft_step); | ||
| 101 | gdp.end += (ft_step - gdp.end % ft_step); | ||
| 102 | rows = (gdp.end - gdp.start) / ft_step + 1; | ||
| 103 | |||
| 104 | gdp.ds_cnt = ds_cnt; | ||
| 105 | data_ptr = 0; | ||
| 106 | |||
| 107 | var rra_end_time = (rrd.getLastUpdate() - (rrd.getLastUpdate() % ft_step)); | ||
| 108 | var rra_start_time = (rra_end_time - (ft_step * (rra_info.row_cnt - 1))); | ||
| 109 | /* here's an error by one if we don't be careful */ | ||
| 110 | var start_offset = (gdp.start + ft_step - rra_start_time) / ft_step; | ||
| 111 | var end_offset = (rra_end_time - gdp.end) / ft_step; | ||
| 112 | |||
| 113 | gdp.data = []; | ||
| 114 | |||
| 115 | for (i = start_offset; i < rra.row_cnt - end_offset; i++) { | ||
| 116 | if (i < 0) { | ||
| 117 | for (var ii = 0; ii < ds_cnt; ii++) | ||
| 118 | gdp.data[data_ptr++] = Number.NaN; | ||
| 119 | } else if (i >= rra.row_cnt) { | ||
| 120 | for (var ii = 0; ii < ds_cnt; ii++) | ||
| 121 | gdp.data[data_ptr++] = Number.NaN; | ||
| 122 | } else { | ||
| 123 | for (var ii = 0; ii < ds_cnt; ii++) | ||
| 124 | gdp.data[data_ptr++] = rra.getEl(i, ii); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | return ft_step; | ||
| 128 | } | ||
| 129 | }; | ||
diff --git a/js/RrdGfxCanvas.js b/js/RrdGfxCanvas.js new file mode 100644 index 0000000..070b806 --- /dev/null +++ b/js/RrdGfxCanvas.js | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * | ||
| 18 | * Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 19 | **/ | ||
| 20 | |||
| 21 | "use strict"; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * RrdGfxCanvas | ||
| 25 | * @constructor | ||
| 26 | */ | ||
| 27 | var RrdGfxCanvas = function(canvasId) | ||
| 28 | { | ||
| 29 | this.canvas = document.getElementById(canvasId); | ||
| 30 | this.ctx = this.canvas.getContext('2d'); | ||
| 31 | }; | ||
| 32 | |||
| 33 | RrdGfxCanvas.prototype.size = function (width, height) | ||
| 34 | { | ||
| 35 | this.canvas.width = width; | ||
| 36 | this.canvas.height = height; | ||
| 37 | }; | ||
| 38 | |||
| 39 | RrdGfxCanvas.prototype.set_dash = function (dashes, n, offset) | ||
| 40 | { | ||
| 41 | |||
| 42 | }; | ||
| 43 | |||
| 44 | RrdGfxCanvas.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
| 45 | { | ||
| 46 | X0 = Math.round(X0); | ||
| 47 | Y0 = Math.round(Y0); | ||
| 48 | X1 = Math.round(X1); | ||
| 49 | Y1 = Math.round(Y1); | ||
| 50 | |||
| 51 | if (Y0 === Y1) { | ||
| 52 | Y0 += 0.5; | ||
| 53 | Y1 += 0.5; | ||
| 54 | } else if (X0 === X1) { | ||
| 55 | X0 += 0.5; | ||
| 56 | X1 += 0.5; | ||
| 57 | } | ||
| 58 | this.ctx.save(); | ||
| 59 | this.ctx.lineWidth = width; | ||
| 60 | this.ctx.strokeStyle = color | ||
| 61 | this.ctx.beginPath(); | ||
| 62 | this.ctx.moveTo(X0, Y0); | ||
| 63 | this.ctx.lineTo(X1, Y1); | ||
| 64 | this.ctx.stroke(); | ||
| 65 | this.ctx.restore(); | ||
| 66 | }; | ||
| 67 | |||
| 68 | RrdGfxCanvas.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
| 69 | { | ||
| 70 | X0 = Math.round(X0); | ||
| 71 | Y0 = Math.round(Y0); | ||
| 72 | X1 = Math.round(X1); | ||
| 73 | Y1 = Math.round(Y1); | ||
| 74 | |||
| 75 | this.ctx.save(); | ||
| 76 | this.ctx.lineWidth = width; | ||
| 77 | this.ctx.strokeStyle = color; | ||
| 78 | |||
| 79 | this.ctx.beginPath(); | ||
| 80 | if (Y0 === Y1) { | ||
| 81 | Y0 += 0.5; | ||
| 82 | Y1 += 0.5; | ||
| 83 | if (X0 > X1) { | ||
| 84 | var swap = X0; | ||
| 85 | X0 = X1; | ||
| 86 | X1 = swap; | ||
| 87 | } | ||
| 88 | this.ctx.moveTo(X0, Y0); | ||
| 89 | var n=0; | ||
| 90 | while(X0<=X1) { | ||
| 91 | if (n%2 === 1) { | ||
| 92 | X0 += dash_on; | ||
| 93 | this.ctx.lineTo(X0, Y0); | ||
| 94 | } else { | ||
| 95 | X0 += dash_off; | ||
| 96 | this.ctx.moveTo(X0, Y0); | ||
| 97 | } | ||
| 98 | n++; | ||
| 99 | } | ||
| 100 | } else if (X0 === X1) { | ||
| 101 | X0 += 0.5; | ||
| 102 | X1 += 0.5; | ||
| 103 | if (Y0 > Y1) { | ||
| 104 | var swap = Y0; | ||
| 105 | Y0 = Y1; | ||
| 106 | Y1 = swap; | ||
| 107 | } | ||
| 108 | this.ctx.moveTo(X0, Y0); | ||
| 109 | var n=0; | ||
| 110 | while(Y0<=Y1) { | ||
| 111 | if (n%2 === 1) { | ||
| 112 | Y0 += dash_on; | ||
| 113 | this.ctx.lineTo(X0, Y0); | ||
| 114 | } else { | ||
| 115 | Y0 += dash_off; | ||
| 116 | this.ctx.moveTo(X0, Y0); | ||
| 117 | } | ||
| 118 | n++; | ||
| 119 | } | ||
| 120 | |||
| 121 | } else { | ||
| 122 | this.ctx.moveTo(X0, Y0); | ||
| 123 | this.ctx.lineTo(X1, Y1); | ||
| 124 | } | ||
| 125 | this.ctx.stroke(); | ||
| 126 | this.ctx.restore(); | ||
| 127 | }; | ||
| 128 | |||
| 129 | RrdGfxCanvas.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
| 130 | { | ||
| 131 | X0 = Math.round(X0)+0.5; | ||
| 132 | X1 = Math.round(X1)+0.5; | ||
| 133 | Y0 = Math.round(Y0)+0.5; | ||
| 134 | Y1 = Math.round(Y1)+0.5; | ||
| 135 | |||
| 136 | this.ctx.save(); | ||
| 137 | this.ctx.beginPath(); | ||
| 138 | this.ctx.lineWidth = width; | ||
| 139 | this.ctx.moveTo(X0, Y0); | ||
| 140 | this.ctx.lineTo(X1, Y0); | ||
| 141 | this.ctx.lineTo(X1, Y1); | ||
| 142 | this.ctx.lineTo(X0, Y1); | ||
| 143 | this.ctx.closePath(); | ||
| 144 | this.ctx.strokeStyle = style; | ||
| 145 | this.ctx.stroke(); | ||
| 146 | this.ctx.restore(); | ||
| 147 | }; | ||
| 148 | |||
| 149 | RrdGfxCanvas.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
| 150 | { | ||
| 151 | X0 = Math.round(X0)+0.5; | ||
| 152 | Y0 = Math.round(Y0)+0.5; | ||
| 153 | X1 = Math.round(X1)+0.5; | ||
| 154 | Y1 = Math.round(Y1)+0.5; | ||
| 155 | X2 = Math.round(X2)+0.5; | ||
| 156 | Y2 = Math.round(Y2)+0.5; | ||
| 157 | this.ctx.fillStyle = color; | ||
| 158 | this.ctx.beginPath(); | ||
| 159 | this.ctx.moveTo(X0, Y0); | ||
| 160 | this.ctx.lineTo(X1, Y1); | ||
| 161 | this.ctx.lineTo(X2, Y2); | ||
| 162 | }; | ||
| 163 | |||
| 164 | RrdGfxCanvas.prototype.add_point = function (x, y) | ||
| 165 | { | ||
| 166 | x = Math.round(x)+0.5; | ||
| 167 | y = Math.round(y)+0.5; | ||
| 168 | this.ctx.lineTo(x, y); | ||
| 169 | }; | ||
| 170 | |||
| 171 | RrdGfxCanvas.prototype.close_path = function () | ||
| 172 | { | ||
| 173 | this.ctx.closePath(); | ||
| 174 | this.ctx.fill(); | ||
| 175 | }; | ||
| 176 | |||
| 177 | RrdGfxCanvas.prototype.stroke_begin = function (width, style) | ||
| 178 | { | ||
| 179 | this.ctx.save(); | ||
| 180 | this.ctx.beginPath(); | ||
| 181 | this.ctx.lineWidth = width; | ||
| 182 | this.ctx.strokeStyle = style; | ||
| 183 | this.ctx.lineCap = 'round'; | ||
| 184 | this.ctx.round = 'round'; | ||
| 185 | }; | ||
| 186 | |||
| 187 | RrdGfxCanvas.prototype.stroke_end = function () | ||
| 188 | { | ||
| 189 | this.ctx.stroke(); | ||
| 190 | this.ctx.restore(); | ||
| 191 | }; | ||
| 192 | |||
| 193 | RrdGfxCanvas.prototype.moveTo = function (x,y) | ||
| 194 | { | ||
| 195 | x = Math.round(x)+0.5; | ||
| 196 | y = Math.round(y)+0.5; | ||
| 197 | this.ctx.moveTo(x, y); | ||
| 198 | }; | ||
| 199 | |||
| 200 | RrdGfxCanvas.prototype.lineTo = function (x,y) | ||
| 201 | { | ||
| 202 | x = Math.round(x)+0.5; | ||
| 203 | y = Math.round(y)+0.5; | ||
| 204 | this.ctx.lineTo(x, y) | ||
| 205 | }; | ||
| 206 | |||
| 207 | RrdGfxCanvas.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
| 208 | { | ||
| 209 | x = Math.round(x); | ||
| 210 | y = Math.round(y); | ||
| 211 | |||
| 212 | this.ctx.save(); | ||
| 213 | this.ctx.font = font.size+'px '+"'"+font.font+"'"; | ||
| 214 | |||
| 215 | switch (h_align) { | ||
| 216 | case RrdGraph.GFX_H_LEFT: | ||
| 217 | this.ctx.textAlign = 'left'; | ||
| 218 | break; | ||
| 219 | case RrdGraph.GFX_H_RIGHT: | ||
| 220 | this.ctx.textAlign = 'right'; | ||
| 221 | break; | ||
| 222 | case RrdGraph.GFX_H_CENTER: | ||
| 223 | this.ctx.textAlign = 'center'; | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | |||
| 227 | switch (v_align) { | ||
| 228 | case RrdGraph.GFX_V_TOP: | ||
| 229 | this.ctx.textBaseline = 'top'; | ||
| 230 | break; | ||
| 231 | case RrdGraph.GFX_V_BOTTOM: | ||
| 232 | this.ctx.textBaseline = 'bottom'; | ||
| 233 | break; | ||
| 234 | case RrdGraph.GFX_V_CENTER: | ||
| 235 | this.ctx.textBaseline = 'middle'; | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | |||
| 239 | this.ctx.fillStyle = color; | ||
| 240 | this.ctx.translate(x,y); | ||
| 241 | this.ctx.rotate(-angle*Math.PI/180.0); | ||
| 242 | this.ctx.fillText(text, 0, 0); | ||
| 243 | this.ctx.restore(); | ||
| 244 | }; | ||
| 245 | |||
| 246 | RrdGfxCanvas.prototype.get_text_width = function(start, font, tabwidth, text) | ||
| 247 | { | ||
| 248 | this.ctx.save(); | ||
| 249 | this.ctx.font = font.size+"px "+font.font; | ||
| 250 | var width = this.ctx.measureText(text); | ||
| 251 | this.ctx.restore(); | ||
| 252 | return width.width; | ||
| 253 | }; | ||
| 254 | |||
diff --git a/js/RrdGfxPdf.js b/js/RrdGfxPdf.js new file mode 100644 index 0000000..9203020 --- /dev/null +++ b/js/RrdGfxPdf.js | |||
| @@ -0,0 +1,1014 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | **/ | ||
| 18 | /******************************************************************************* | ||
| 19 | * FPDF * | ||
| 20 | * * | ||
| 21 | * Version: 1.7 * | ||
| 22 | * Date: 2011-06-18 * | ||
| 23 | * Author: Olivier PLATHEY * | ||
| 24 | *******************************************************************************/ | ||
| 25 | |||
| 26 | // define('FPDF_VERSION','1.7'); | ||
| 27 | |||
| 28 | "use strict"; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * RrdGfxPdf | ||
| 32 | * @constructor | ||
| 33 | */ | ||
| 34 | var RrdGfxPdf = function (orientation, unit, size) | ||
| 35 | { | ||
| 36 | if (orientation === undefined) | ||
| 37 | orientation='P'; | ||
| 38 | if (unit === undefined) | ||
| 39 | unit='mm'; | ||
| 40 | if (size === undefined) | ||
| 41 | size='A4'; | ||
| 42 | |||
| 43 | this.lMargin = 0; // left margin | ||
| 44 | this.tMargin = 0; // top margin | ||
| 45 | this.rMargin = 0; // right margin | ||
| 46 | this.bMargin = 0; // page break margin | ||
| 47 | this.cMargin = 0; // cell margin | ||
| 48 | |||
| 49 | this.x = 0; // current position in user unit | ||
| 50 | this.y = 0; | ||
| 51 | |||
| 52 | this.ZoomMode = null; // zoom display mode | ||
| 53 | this.LayoutMode = null; // layout display mode | ||
| 54 | this.title = null; // title | ||
| 55 | this.subject = null; // subject | ||
| 56 | this.author = null; // author | ||
| 57 | this.keywords = null; // keywords | ||
| 58 | this.creator = null; // creator | ||
| 59 | |||
| 60 | // Initialization of properties | ||
| 61 | this.page = 0; // current page number | ||
| 62 | this.offsets = []; // array of object offsets | ||
| 63 | this.n = 2; // current object number | ||
| 64 | this.buffer = ''; // buffer holding in-memory PDF | ||
| 65 | this.pages = []; // array containing pages | ||
| 66 | this.PageSizes = []; // used for pages with non default sizes or orientations | ||
| 67 | this.state = 0; // current document state | ||
| 68 | this.fonts = {}; // array of used fonts | ||
| 69 | this.diffs = []; // array of encoding differences | ||
| 70 | this.FontFamily = ''; // current font family | ||
| 71 | this.FontStyle = ''; // current font style | ||
| 72 | this.FontSizePt = 12; // current font size in points | ||
| 73 | this.FontSize = this.FontSizePt/this.k; | ||
| 74 | this.DrawColor = '0 G'; // commands for drawing color | ||
| 75 | this.FillColor = '0 g'; // commands for filling color | ||
| 76 | this.TextColor = '0 g'; // commands for text color | ||
| 77 | this.ColorFlag = false; // indicates whether fill and text colors are different | ||
| 78 | this.ws = 0; // word spacing | ||
| 79 | |||
| 80 | // Core fonts | ||
| 81 | this.CoreFonts = ['courier', 'helvetica', 'times', 'symbol', 'zapfdingbats']; | ||
| 82 | // Scale factor (number of points in user unit) | ||
| 83 | if(unit === 'pt') | ||
| 84 | this.k = 1; | ||
| 85 | else if(unit === 'mm') | ||
| 86 | this.k = 72/25.4; | ||
| 87 | else if(unit === 'cm') | ||
| 88 | this.k = 72/2.54; | ||
| 89 | else if(unit === 'in') | ||
| 90 | this.k = 72; | ||
| 91 | else | ||
| 92 | throw 'Incorrect unit: '+unit; | ||
| 93 | // Page sizes | ||
| 94 | this.StdPageSizes = { | ||
| 95 | 'a3': [841.89 , 1190.55], | ||
| 96 | 'a4': [595.28 , 841.89], | ||
| 97 | 'a5': [420.94 , 595.28], | ||
| 98 | 'letter': [612 , 792], | ||
| 99 | 'legal': [612 , 1008] | ||
| 100 | }; | ||
| 101 | |||
| 102 | size = this._getpagesize(size); | ||
| 103 | this.DefPageSize = size; | ||
| 104 | this.CurPageSize = size; | ||
| 105 | // Page orientation | ||
| 106 | orientation = orientation.toLowerCase(); | ||
| 107 | if(orientation=='p' || orientation=='portrait') { | ||
| 108 | this.DefOrientation = 'P'; | ||
| 109 | this.w = size[0]; | ||
| 110 | this.h = size[1]; | ||
| 111 | } else if(orientation=='l' || orientation=='landscape') { | ||
| 112 | this.DefOrientation = 'L'; | ||
| 113 | this.w = size[1]; | ||
| 114 | this.h = size[0]; | ||
| 115 | } else { | ||
| 116 | throw 'Incorrect orientation: '+orientation; | ||
| 117 | } | ||
| 118 | this.CurOrientation = this.DefOrientation; | ||
| 119 | this.wPt = this.w*this.k; | ||
| 120 | this.hPt = this.h*this.k; | ||
| 121 | // Page margins (1 cm) | ||
| 122 | var margin = 28.35/this.k; | ||
| 123 | this.SetMargins(margin,margin); | ||
| 124 | // Interior cell margin (1 mm) | ||
| 125 | this.cMargin = margin/10; | ||
| 126 | // Line width (0.2 mm) | ||
| 127 | this.LineWidth = .567/this.k; | ||
| 128 | // Default display mode | ||
| 129 | this.SetDisplayMode('default'); | ||
| 130 | // Set default PDF version number | ||
| 131 | this.PDFVersion = '1.3'; | ||
| 132 | }; | ||
| 133 | |||
| 134 | RrdGfxPdf.CORE_FONTS= { | ||
| 135 | 'courierBI': {name: 'Courier-BoldOblique', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
| 136 | 'courierB': {name: 'Courier-Bold', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
| 137 | 'courierI': {name: 'Courier-Oblique', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
| 138 | 'courier': {name: 'Courier', up: -100, ut: 50, cw: [600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600] }, | ||
| 139 | 'helveticaBI': {name: 'Helvetica-BoldOblique', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722, 722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889, 611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556, 500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000, 556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611, 611,611,611,611,611,584,611,611,611,611,611,556,611,556] }, | ||
| 140 | 'helveticaB': {name: 'Helvetica-Bold', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722, 722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889, 611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556, 500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000, 556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611, 611,611,611,611,611,584,611,611,611,611,611,556,611,556] }, | ||
| 141 | 'helveticaI': {name: 'Helvetica-Oblique', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667, 667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833, 556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556, 333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000, 500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556, 556,556,556,556,556,584,611,556,556,556,556,500,556,500] }, | ||
| 142 | 'helvetica': {name: 'Helvetica', up: -100, ut: 50, cw: [ 278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584, 278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667, 667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944, 667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833, 556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556, 333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000, 500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333, 400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667, 1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722, 722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556, 556,556,556,556,556,584,611,556,556,556,556,500,556,500] }, | ||
| 143 | 'timesBI': {name: 'Times-BoldItalic', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667, 667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889, 667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778, 556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500, 500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000, 389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333, 400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667, 944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722, 722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556, 500,500,500,500,500,570,500,556,556,556,556,444,500,444] }, | ||
| 144 | 'timesB': {name: 'Times-Bold', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722, 667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000, 722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833, 556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500, 500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000, 389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333, 400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722, 1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722, 722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556, 500,500,500,500,500,570,500,556,556,556,556,500,556,500] }, | ||
| 145 | 'timesI': {name: 'Times-Italic', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611, 611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833, 611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722, 500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500, 556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980, 389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333, 400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611, 889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722, 722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500, 500,500,500,500,500,675,500,500,500,500,500,444,500,444] }, | ||
| 146 | 'times': {name: 'Times-Roman', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564, 250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722, 667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944, 722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778, 500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500, 444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980, 389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333, 400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722, 889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722, 722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500, 500,500,500,500,500,564,500,500,500,500,500,500,500,500] }, | ||
| 147 | 'symbol': {name: 'Symbol', up: -100, ut: 50, cw: [ 250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, 250,250,250,250,250,250,250,250,250,250,250,333,713,500,549,833,778,439,333,333,500,549, 250,549,250,278,500,500,500,500,500,500,500,500,500,500,278,278,549,549,549,444,549,722, 667,722,612,611,763,603,722,333,631,722,686,889,722,722,768,741,556,592,611,690,439,768, 645,795,611,333,863,333,658,500,500,631,549,549,494,439,521,411,603,329,603,549,549,576, 521,549,549,521,549,603,439,576,713,686,493,686,494,480,200,480,549,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,750,620,247,549,167,713,500,753,753,753,753,1042,987,603,987,603, 400,549,411,549,549,713,494,460,549,549,549,549,1000,603,1000,658,823,686,795,987,768,768, 823,768,768,713,713,713,713,713,713,713,768,713,790,790,890,823,549,250,713,603,603,1042, 987,603,987,603,494,329,790,790,786,713,384,384,384,384,384,384,494,494,494,494,0,329,274,686,686,686,384,384,384,384,384,384,494,494,494,0] }, | ||
| 148 | 'zapfdingbats': {name: 'ZapfDingbats', up: -100, ut: 50, cw: [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939, 549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692, 786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776, 768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873, 761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317, 276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788, 788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788, 788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918, 927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874, 760,946,771,865,771,888,967,888,831,873,927,970,918,0] } | ||
| 149 | }; | ||
| 150 | |||
| 151 | RrdGfxPdf.prototype.parse_color = function(str) | ||
| 152 | { | ||
| 153 | var bits; | ||
| 154 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) { | ||
| 155 | return [parseInt(bits[1]+bits[1], 16), parseInt(bits[2]+bits[2], 16), parseInt(bits[3]+bits[3], 16), 1.0]; | ||
| 156 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 157 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), 1.0]; | ||
| 158 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 159 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), parseInt(bits[4], 16)/255]; | ||
| 160 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) { | ||
| 161 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), 1.0]; | ||
| 162 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) { | ||
| 163 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), parseFloat(bits[4], 10)]; | ||
| 164 | } else { | ||
| 165 | throw "Unknow color format '"+str+"'"; | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 169 | RrdGfxPdf.prototype.size = function (width, height) | ||
| 170 | { | ||
| 171 | var size = [height, width]; | ||
| 172 | this.DefPageSize = size; | ||
| 173 | this.CurPageSize = size; | ||
| 174 | this.DefOrientation = 'L'; | ||
| 175 | this.w = size[1]; | ||
| 176 | this.h = size[0]; | ||
| 177 | this.CurOrientation = this.DefOrientation; | ||
| 178 | this.wPt = this.w*this.k; | ||
| 179 | this.hPt = this.h*this.k; | ||
| 180 | this.AddPage(); | ||
| 181 | }; | ||
| 182 | |||
| 183 | RrdGfxPdf.prototype.set_dash = function (dashes, n, offset) | ||
| 184 | { | ||
| 185 | }; | ||
| 186 | |||
| 187 | RrdGfxPdf.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
| 188 | { | ||
| 189 | this._save(); | ||
| 190 | this._setLineWidth(width); | ||
| 191 | var rgba = this.parse_color(color); | ||
| 192 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
| 193 | this._moveTo(X0, Y0); | ||
| 194 | this._lineTo(X1, Y1); | ||
| 195 | this._stroke(); | ||
| 196 | this._restore(); | ||
| 197 | }; | ||
| 198 | |||
| 199 | RrdGfxPdf.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
| 200 | { | ||
| 201 | this._save(); | ||
| 202 | this._setLineWidth(width); | ||
| 203 | var rgba = this.parse_color(color); | ||
| 204 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
| 205 | this._out('['+(dash_on*this.k)+' '+(dash_off*this.k)+'] 0 d'); | ||
| 206 | this._moveTo(X0, Y0); | ||
| 207 | this._lineTo(X1, Y1); | ||
| 208 | this._stroke(); | ||
| 209 | this._restore(); | ||
| 210 | }; | ||
| 211 | |||
| 212 | RrdGfxPdf.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
| 213 | { | ||
| 214 | this._save(); | ||
| 215 | this._setLineWidth(width); | ||
| 216 | var rgba = this.parse_color(style); | ||
| 217 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
| 218 | this._moveTo(X0, Y0); | ||
| 219 | this._lineTo(X1, Y0); | ||
| 220 | this._lineTo(X1, Y1); | ||
| 221 | this._lineTo(X0, Y1); | ||
| 222 | this._closePath(); | ||
| 223 | this._stroke(); | ||
| 224 | this._restore(); | ||
| 225 | }; | ||
| 226 | |||
| 227 | RrdGfxPdf.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
| 228 | { | ||
| 229 | var rgba = this.parse_color(color); | ||
| 230 | this._setFillColor(rgba[0], rgba[1], rgba[2]); | ||
| 231 | this._moveTo(X0, Y0); | ||
| 232 | this._lineTo(X1, Y1); | ||
| 233 | this._lineTo(X2, Y2); | ||
| 234 | }; | ||
| 235 | |||
| 236 | RrdGfxPdf.prototype.add_point = function (x, y) | ||
| 237 | { | ||
| 238 | this._lineTo(x, y); | ||
| 239 | }; | ||
| 240 | |||
| 241 | RrdGfxPdf.prototype.close_path = function () | ||
| 242 | { | ||
| 243 | this._closePath(); | ||
| 244 | this._fill(); | ||
| 245 | }; | ||
| 246 | |||
| 247 | RrdGfxPdf.prototype.stroke_begin = function (width, style) | ||
| 248 | { | ||
| 249 | this._save(); | ||
| 250 | this._setLineWidth(width); | ||
| 251 | var rgba = this.parse_color(style); | ||
| 252 | this._setDrawColor(rgba[0], rgba[1], rgba[2]); | ||
| 253 | this._out('0 J'); // line cap | ||
| 254 | this._out('0 j'); // line join | ||
| 255 | }; | ||
| 256 | |||
| 257 | RrdGfxPdf.prototype.stroke_end = function () | ||
| 258 | { | ||
| 259 | this._stroke(); | ||
| 260 | this._restore(); | ||
| 261 | }; | ||
| 262 | |||
| 263 | RrdGfxPdf.prototype.moveTo = function (x,y) | ||
| 264 | { | ||
| 265 | this._moveTo(x, y); | ||
| 266 | }; | ||
| 267 | |||
| 268 | RrdGfxPdf.prototype.lineTo = function (x,y) | ||
| 269 | { | ||
| 270 | this._lineTo(x, y) | ||
| 271 | }; | ||
| 272 | |||
| 273 | RrdGfxPdf.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
| 274 | { | ||
| 275 | this._save(); | ||
| 276 | this._setFont('courier', '', font.size*this.k); | ||
| 277 | |||
| 278 | var width = this._getStringWidth('courier', '', font.size*this.k, text); | ||
| 279 | var height = font.size; | ||
| 280 | /* | ||
| 281 | this._moveTo(x,y-5); this._lineTo(x,y+5); | ||
| 282 | this._moveTo(x-5,y); this._lineTo(x+5,y); | ||
| 283 | this._moveTo(x+width,y-height-5); this._lineTo(x+width,y-height+5); | ||
| 284 | this._moveTo(x+width-5,y-height); this._lineTo(x+width+5,y-height); | ||
| 285 | this._stroke(); | ||
| 286 | */ | ||
| 287 | switch (h_align) { | ||
| 288 | case RrdGraph.GFX_H_LEFT: | ||
| 289 | if (angle == -90) { | ||
| 290 | x = x-height/2; | ||
| 291 | } | ||
| 292 | break; | ||
| 293 | case RrdGraph.GFX_H_RIGHT: | ||
| 294 | x = x-width; | ||
| 295 | break; | ||
| 296 | case RrdGraph.GFX_H_CENTER: | ||
| 297 | if (angle != 90) { | ||
| 298 | x = x-width/2; | ||
| 299 | } | ||
| 300 | break; | ||
| 301 | } | ||
| 302 | |||
| 303 | switch (v_align) { | ||
| 304 | case RrdGraph.GFX_V_TOP: | ||
| 305 | if (angle != -90) { | ||
| 306 | y = y + height/2; | ||
| 307 | } | ||
| 308 | break; | ||
| 309 | case RrdGraph.GFX_V_BOTTOM: | ||
| 310 | y = y - height/3; | ||
| 311 | break; | ||
| 312 | case RrdGraph.GFX_V_CENTER: | ||
| 313 | if (angle == 90) { | ||
| 314 | y = y + width/2; | ||
| 315 | } else { | ||
| 316 | y = y + height/4; | ||
| 317 | } | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | |||
| 321 | x = x*this.k; | ||
| 322 | y = (this.h-y)*this.k | ||
| 323 | |||
| 324 | var tm = []; | ||
| 325 | tm[0] = Math.cos(angle*Math.PI/180.0); | ||
| 326 | tm[1] = Math.sin(angle*Math.PI/180.0); | ||
| 327 | tm[2] = -tm[1]; | ||
| 328 | tm[3] = tm[0]; | ||
| 329 | |||
| 330 | tm[4] = x + (tm[1] * y) - (tm[0] * x); | ||
| 331 | tm[5] = y - (tm[0] * y) - (tm[1] * x); | ||
| 332 | |||
| 333 | var rgba = this.parse_color(color); | ||
| 334 | this._save(); | ||
| 335 | this._out('BT'); | ||
| 336 | this._out(sprintf('%.3F %.3F %.3F rg', rgba[0]/255,rgba[1]/255,rgba[2]/255)); | ||
| 337 | this._out(sprintf('%.2F %.2F Td', x, y)); | ||
| 338 | this._out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', tm[0], tm[1], tm[2], tm[3], tm[4], tm[5])); | ||
| 339 | this._out(sprintf('(%s) Tj',this._escape(text))); | ||
| 340 | this._out('ET'); | ||
| 341 | this._restore(); | ||
| 342 | }; | ||
| 343 | |||
| 344 | RrdGfxPdf.prototype.get_text_width = function(start, font, tabwidth, text) | ||
| 345 | { | ||
| 346 | var width = this._getStringWidth('courier', '', font.size*this.k, text); | ||
| 347 | return width; | ||
| 348 | }; | ||
| 349 | |||
| 350 | /**** Public methods *****/ | ||
| 351 | |||
| 352 | RrdGfxPdf.prototype.SetMargins = function(left, top, right) | ||
| 353 | { | ||
| 354 | if (right === undefined) | ||
| 355 | right = null; | ||
| 356 | // Set left, top and right margins | ||
| 357 | this.lMargin = left; | ||
| 358 | this.tMargin = top; | ||
| 359 | if(right===null) | ||
| 360 | right = left; | ||
| 361 | this.rMargin = right; | ||
| 362 | }; | ||
| 363 | |||
| 364 | RrdGfxPdf.prototype.SetLeftMargin = function(margin) | ||
| 365 | { | ||
| 366 | // Set left margin | ||
| 367 | this.lMargin = margin; | ||
| 368 | if(this.page>0 && this.x<margin) | ||
| 369 | this.x = margin; | ||
| 370 | }; | ||
| 371 | |||
| 372 | RrdGfxPdf.prototype.SetTopMargin = function(margin) | ||
| 373 | { | ||
| 374 | // Set top margin | ||
| 375 | this.tMargin = margin; | ||
| 376 | }; | ||
| 377 | |||
| 378 | RrdGfxPdf.prototype.SetRightMargin = function(margin) | ||
| 379 | { | ||
| 380 | // Set right margin | ||
| 381 | this.rMargin = margin; | ||
| 382 | }; | ||
| 383 | |||
| 384 | RrdGfxPdf.prototype.SetDisplayMode = function(zoom, layout) | ||
| 385 | { | ||
| 386 | // Set display mode in viewer | ||
| 387 | if(zoom === 'fullpage' || zoom === 'fullwidth' || zoom === 'real' || zoom == 'default' || !(typeof zoom === "string")) | ||
| 388 | this.ZoomMode = zoom; | ||
| 389 | else | ||
| 390 | throw 'Incorrect zoom display mode: '+zoom; | ||
| 391 | |||
| 392 | if(layout === undefined) { | ||
| 393 | this.LayoutMode = 'default'; | ||
| 394 | } else if(layout === 'single' || layout === 'continuous' || layout === 'two' || layout === 'default') { | ||
| 395 | this.LayoutMode = layout; | ||
| 396 | } else { | ||
| 397 | throw 'Incorrect layout display mode: '+layout; | ||
| 398 | } | ||
| 399 | }; | ||
| 400 | |||
| 401 | RrdGfxPdf.prototype.SetTitle = function(title) | ||
| 402 | { | ||
| 403 | // Title of document | ||
| 404 | this.title = title; | ||
| 405 | }; | ||
| 406 | |||
| 407 | RrdGfxPdf.prototype.SetSubject = function(subject) | ||
| 408 | { | ||
| 409 | // Subject of document | ||
| 410 | this.subject = subject; | ||
| 411 | }; | ||
| 412 | |||
| 413 | RrdGfxPdf.prototype.SetAuthor = function(author) | ||
| 414 | { | ||
| 415 | // Author of document | ||
| 416 | this.author = author; | ||
| 417 | }; | ||
| 418 | |||
| 419 | RrdGfxPdf.prototype.SetKeywords = function(keywords) | ||
| 420 | { | ||
| 421 | // Keywords of document | ||
| 422 | this.keywords = keywords; | ||
| 423 | }; | ||
| 424 | |||
| 425 | RrdGfxPdf.prototype.SetCreator = function(creator) | ||
| 426 | { | ||
| 427 | // Creator of document | ||
| 428 | this.creator = creator; | ||
| 429 | }; | ||
| 430 | |||
| 431 | RrdGfxPdf.prototype.Open = function() | ||
| 432 | { | ||
| 433 | // Begin document | ||
| 434 | this.state = 1; | ||
| 435 | }; | ||
| 436 | |||
| 437 | RrdGfxPdf.prototype.Close = function() | ||
| 438 | { | ||
| 439 | // Terminate document | ||
| 440 | if(this.state==3) | ||
| 441 | return; | ||
| 442 | if(this.page==0) | ||
| 443 | this.AddPage(); | ||
| 444 | // Close page | ||
| 445 | this._endpage(); | ||
| 446 | // Close document | ||
| 447 | this._enddoc(); | ||
| 448 | }; | ||
| 449 | |||
| 450 | RrdGfxPdf.prototype.AddPage = function(orientation, size) | ||
| 451 | { | ||
| 452 | if (orientation === undefined) orientation=''; | ||
| 453 | if (size === undefined) size=''; | ||
| 454 | |||
| 455 | // Start a new page | ||
| 456 | if(this.state==0) | ||
| 457 | this.Open(); | ||
| 458 | |||
| 459 | var family = this.FontFamily; | ||
| 460 | var style = this.FontStyle; | ||
| 461 | var fontsize = this.FontSizePt; | ||
| 462 | var lw = this.LineWidth; | ||
| 463 | var dc = this.DrawColor; | ||
| 464 | var fc = this.FillColor; | ||
| 465 | var tc = this.TextColor; | ||
| 466 | var cf = this.ColorFlag; | ||
| 467 | |||
| 468 | if(this.page>0) | ||
| 469 | { | ||
| 470 | // Close page | ||
| 471 | this._endpage(); | ||
| 472 | } | ||
| 473 | // Start new page | ||
| 474 | this._beginpage(orientation,size); | ||
| 475 | // Set line cap style to square | ||
| 476 | this._out('2 J'); | ||
| 477 | // Set line width | ||
| 478 | this.LineWidth = lw; | ||
| 479 | this._out(sprintf('%.2F w',lw*this.k)); | ||
| 480 | // Set font | ||
| 481 | if(family) | ||
| 482 | this._setFont(family,style,fontsize); | ||
| 483 | // Set colors | ||
| 484 | this.DrawColor = dc; | ||
| 485 | if(dc!='0 G') | ||
| 486 | this._out(dc); | ||
| 487 | this.FillColor = fc; | ||
| 488 | if(fc!='0 g') | ||
| 489 | this._out(fc); | ||
| 490 | this.TextColor = tc; | ||
| 491 | this.ColorFlag = cf; | ||
| 492 | // Restore line width | ||
| 493 | if(this.LineWidth!=lw) | ||
| 494 | { | ||
| 495 | this.LineWidth = lw; | ||
| 496 | this._out(sprintf('%.2F w',lw*this.k)); | ||
| 497 | } | ||
| 498 | // Restore font | ||
| 499 | if(family) | ||
| 500 | this._setFont(family,style,fontsize); | ||
| 501 | // Restore colors | ||
| 502 | if(this.DrawColor!=dc) | ||
| 503 | { | ||
| 504 | this.DrawColor = dc; | ||
| 505 | this._out(dc); | ||
| 506 | } | ||
| 507 | if(this.FillColor!=fc) | ||
| 508 | { | ||
| 509 | this.FillColor = fc; | ||
| 510 | this._out(fc); | ||
| 511 | } | ||
| 512 | this.TextColor = tc; | ||
| 513 | this.ColorFlag = cf; | ||
| 514 | }; | ||
| 515 | |||
| 516 | RrdGfxPdf.prototype.PageNo = function() | ||
| 517 | { | ||
| 518 | // Get current page number | ||
| 519 | return this.page; | ||
| 520 | }; | ||
| 521 | |||
| 522 | RrdGfxPdf.prototype._setDrawColor = function(r, g, b) | ||
| 523 | { | ||
| 524 | if (g === undefined) g=null; | ||
| 525 | if (b === undefined) b=null; | ||
| 526 | // Set color for all stroking operations | ||
| 527 | if((r==0 && g==0 && b==0) || g===null) | ||
| 528 | this.DrawColor = sprintf('%.3F G',r/255); | ||
| 529 | else | ||
| 530 | this.DrawColor = sprintf('%.3F %.3F %.3F RG',r/255,g/255,b/255); | ||
| 531 | if(this.page>0) | ||
| 532 | this._out(this.DrawColor); | ||
| 533 | }; | ||
| 534 | |||
| 535 | RrdGfxPdf.prototype._setFillColor = function(r, g, b) | ||
| 536 | { | ||
| 537 | if (g === undefined) g=null; | ||
| 538 | if (b === undefined) b=null; | ||
| 539 | // Set color for all filling operations | ||
| 540 | if((r==0 && g==0 && b==0) || g===null) | ||
| 541 | this.FillColor = sprintf('%.3F g',r/255); | ||
| 542 | else | ||
| 543 | this.FillColor = sprintf('%.3F %.3F %.3F rg',r/255,g/255,b/255); | ||
| 544 | this.ColorFlag = (this.FillColor!=this.TextColor); | ||
| 545 | if(this.page>0) | ||
| 546 | this._out(this.FillColor); | ||
| 547 | }; | ||
| 548 | |||
| 549 | RrdGfxPdf.prototype._setTextColor = function(r, g, b) | ||
| 550 | { | ||
| 551 | if (g === undefined) g=null; | ||
| 552 | if (b === undefined) b=null; | ||
| 553 | // Set color for text | ||
| 554 | if((r==0 && g==0 && b==0) || g===null) | ||
| 555 | this.TextColor = sprintf('%.3F g',r/255); | ||
| 556 | else | ||
| 557 | this.TextColor = sprintf('%.3F %.3F %.3F rg',r/255,g/255,b/255); | ||
| 558 | this.ColorFlag = (this.FillColor!=this.TextColor); | ||
| 559 | }; | ||
| 560 | |||
| 561 | RrdGfxPdf.prototype._getStringWidth = function(family, style, size, s) | ||
| 562 | { | ||
| 563 | if (style === undefined) style = ''; | ||
| 564 | if (size === undefined) size = 0; | ||
| 565 | // Select a font; size given in points | ||
| 566 | |||
| 567 | if(family=='') family = this.FontFamily; | ||
| 568 | else family = family.toLowerCase(); | ||
| 569 | |||
| 570 | style = style.toUpperCase(); | ||
| 571 | if(style=='IB') style = 'BI'; | ||
| 572 | |||
| 573 | if(size==0) size = this.FontSizePt; | ||
| 574 | |||
| 575 | // Test if font is already loaded | ||
| 576 | var fontkey = family+style; | ||
| 577 | if(!(fontkey in this.fonts)) { | ||
| 578 | // Test if one of the core fonts | ||
| 579 | if(family=='arial') family = 'helvetica'; | ||
| 580 | if(family=='symbol' || family=='zapfdingbats') style = ''; | ||
| 581 | fontkey = family+style; | ||
| 582 | |||
| 583 | if (!(fontkey in this.fonts)) | ||
| 584 | this.AddFont(family, style); | ||
| 585 | } | ||
| 586 | // Select it | ||
| 587 | size = size/this.k; | ||
| 588 | var cw = this.fonts[fontkey].cw; | ||
| 589 | var w = 0; | ||
| 590 | var l = s.length; | ||
| 591 | for(var i=0; i<l; i++) { | ||
| 592 | w += cw[s.charCodeAt(i)]; | ||
| 593 | } | ||
| 594 | return w*size/1000; | ||
| 595 | }; | ||
| 596 | |||
| 597 | RrdGfxPdf.prototype._setLineWidth = function(width) | ||
| 598 | { | ||
| 599 | // Set line width | ||
| 600 | this.LineWidth = width; | ||
| 601 | if(this.page>0) | ||
| 602 | this._out(sprintf('%.2F w',width*this.k)); | ||
| 603 | }; | ||
| 604 | |||
| 605 | RrdGfxPdf.prototype._moveTo = function(x, y) | ||
| 606 | { | ||
| 607 | this._out(sprintf('%.2F %.2F m',x*this.k,(this.h-y)*this.k)); | ||
| 608 | }; | ||
| 609 | |||
| 610 | RrdGfxPdf.prototype._lineTo = function(x, y) | ||
| 611 | { | ||
| 612 | this._out(sprintf('%.2F %.2F l',x*this.k,(this.h-y)*this.k)); | ||
| 613 | }; | ||
| 614 | |||
| 615 | RrdGfxPdf.prototype._stroke = function() | ||
| 616 | { | ||
| 617 | this._out('S'); | ||
| 618 | }; | ||
| 619 | |||
| 620 | RrdGfxPdf.prototype._save = function() | ||
| 621 | { | ||
| 622 | this._out('q'); | ||
| 623 | }; | ||
| 624 | |||
| 625 | RrdGfxPdf.prototype._restore = function() | ||
| 626 | { | ||
| 627 | this._out('Q'); | ||
| 628 | }; | ||
| 629 | |||
| 630 | RrdGfxPdf.prototype._closePath = function() | ||
| 631 | { | ||
| 632 | this._out('h'); | ||
| 633 | }; | ||
| 634 | |||
| 635 | RrdGfxPdf.prototype._fill = function() | ||
| 636 | { | ||
| 637 | this._out('f'); | ||
| 638 | }; | ||
| 639 | |||
| 640 | RrdGfxPdf.prototype._line = function(x1, y1, x2, y2) | ||
| 641 | { | ||
| 642 | // Draw a line | ||
| 643 | this._out(sprintf('%.2F %.2F m %.2F %.2F l S',x1*this.k,(this.h-y1)*this.k,x2*this.k,(this.h-y2)*this.k)); | ||
| 644 | }; | ||
| 645 | |||
| 646 | RrdGfxPdf.prototype._rect = function(x, y, w, h, style) | ||
| 647 | { | ||
| 648 | var op; | ||
| 649 | // Draw a rectangle | ||
| 650 | if(style=='F') | ||
| 651 | op = 'f'; | ||
| 652 | else if(style=='FD' || style=='DF') | ||
| 653 | op = 'B'; | ||
| 654 | else | ||
| 655 | op = 'S'; | ||
| 656 | this._out(sprintf('%.2F %.2F %.2F %.2F re %s',x*this.k,(this.h-y)*this.k,w*this.k,-h*this.k,op)); | ||
| 657 | }; | ||
| 658 | |||
| 659 | RrdGfxPdf.prototype.AddFont = function (family, style, file) | ||
| 660 | { | ||
| 661 | if (style === undefined) style = ''; | ||
| 662 | |||
| 663 | if(family=='') family = this.FontFamily; | ||
| 664 | else family = family.toLowerCase(); | ||
| 665 | |||
| 666 | style = style.toUpperCase(); | ||
| 667 | if(style=='IB') style = 'BI'; | ||
| 668 | |||
| 669 | var fontkey = family+style; | ||
| 670 | if(fontkey in this.fonts) | ||
| 671 | return; | ||
| 672 | |||
| 673 | if(fontkey in RrdGfxPdf.CORE_FONTS){ | ||
| 674 | var font = RrdGfxPdf.CORE_FONTS[fontkey]; | ||
| 675 | this.fonts[fontkey] = font; | ||
| 676 | var i=0; | ||
| 677 | for (var n in this.fonts) i++; | ||
| 678 | font['i'] = i; | ||
| 679 | } else { | ||
| 680 | throw 'Undefined font: '+family+' '+style; | ||
| 681 | } | ||
| 682 | }; | ||
| 683 | |||
| 684 | RrdGfxPdf.prototype._setFont = function(family, style, size) | ||
| 685 | { | ||
| 686 | if (style === undefined) style = ''; | ||
| 687 | if (size === undefined) size = 0; | ||
| 688 | // Select a font; size given in points | ||
| 689 | |||
| 690 | if(family=='') family = this.FontFamily; | ||
| 691 | else family = family.toLowerCase(); | ||
| 692 | |||
| 693 | style = style.toUpperCase(); | ||
| 694 | if(style=='IB') style = 'BI'; | ||
| 695 | |||
| 696 | if(size==0) size = this.FontSizePt; | ||
| 697 | |||
| 698 | // Test if font is already selected | ||
| 699 | //if(this.FontFamily==family && this.FontStyle==style && this.FontSizePt==size) | ||
| 700 | // return; | ||
| 701 | |||
| 702 | // Test if font is already loaded | ||
| 703 | var fontkey = family+style; | ||
| 704 | if(!(fontkey in this.fonts)) { | ||
| 705 | // Test if one of the core fonts | ||
| 706 | if(family=='arial') family = 'helvetica'; | ||
| 707 | if(family=='symbol' || family=='zapfdingbats') style = ''; | ||
| 708 | fontkey = family+style; | ||
| 709 | |||
| 710 | if (!(fontkey in this.fonts)) | ||
| 711 | this.AddFont(family, style); | ||
| 712 | } | ||
| 713 | // Select it | ||
| 714 | this.FontFamily = family; | ||
| 715 | this.FontStyle = style; | ||
| 716 | this.FontSizePt = size; | ||
| 717 | this.FontSize = size/this.k; | ||
| 718 | this.CurrentFont = this.fonts[fontkey]; | ||
| 719 | if(this.page>0) | ||
| 720 | this._out(sprintf('BT /F%d %.2F Tf ET',this.CurrentFont['i'],this.FontSizePt)); // FIXME i | ||
| 721 | }; | ||
| 722 | |||
| 723 | RrdGfxPdf.prototype._setFontSize = function(size) | ||
| 724 | { | ||
| 725 | // Set font size in points | ||
| 726 | //if(this.FontSizePt==size) | ||
| 727 | // return; | ||
| 728 | this.FontSizePt = size; | ||
| 729 | this.FontSize = size/this.k; | ||
| 730 | if(this.page>0) | ||
| 731 | this._out(sprintf('BT /F%d %.2F Tf ET',this.CurrentFont['i'],this.FontSizePt)); | ||
| 732 | }; | ||
| 733 | |||
| 734 | RrdGfxPdf.prototype._text = function(x, y, txt) | ||
| 735 | { | ||
| 736 | // Output a string | ||
| 737 | var s = sprintf('BT %.2F %.2F Td (%s) Tj ET',x*this.k,(this.h-y)*this.k,this._escape(txt)); | ||
| 738 | if(this.ColorFlag) | ||
| 739 | s = 'q '+this.TextColor+' '+s+' Q'; | ||
| 740 | this._out(s); | ||
| 741 | }; | ||
| 742 | |||
| 743 | RrdGfxPdf.prototype.output = function() | ||
| 744 | { | ||
| 745 | // Output PDF to some destination | ||
| 746 | if(this.state<3) | ||
| 747 | this.Close(); | ||
| 748 | document.location.href = 'data:application/pdf;base64,' + Base64.encode(this.buffer); | ||
| 749 | //return this.buffer; | ||
| 750 | }; | ||
| 751 | |||
| 752 | RrdGfxPdf.prototype._getpagesize = function(size) // FIXME | ||
| 753 | { | ||
| 754 | if(typeof size === "string" ) { | ||
| 755 | size = size.toLowerCase(); | ||
| 756 | if(!(size in this.StdPageSizes)) | ||
| 757 | throw 'Unknown page size: '+size; | ||
| 758 | var a = this.StdPageSizes[size]; | ||
| 759 | return [a[0]/this.k, a[1]/this.k]; | ||
| 760 | } else { | ||
| 761 | if(size[0]>size[1]) { | ||
| 762 | return [size[1], size[0]]; | ||
| 763 | } else { | ||
| 764 | return size; | ||
| 765 | } | ||
| 766 | } | ||
| 767 | }; | ||
| 768 | |||
| 769 | RrdGfxPdf.prototype._beginpage = function(orientation, size) | ||
| 770 | { | ||
| 771 | this.page++; | ||
| 772 | this.pages[this.page] = ''; | ||
| 773 | this.state = 2; | ||
| 774 | this.x = this.lMargin; | ||
| 775 | this.y = this.tMargin; | ||
| 776 | this.FontFamily = ''; | ||
| 777 | // Check page size and orientation | ||
| 778 | if(orientation=='') orientation = this.DefOrientation; | ||
| 779 | else orientation = strtoupper(orientation[0]); | ||
| 780 | |||
| 781 | if(size=='') size = this.DefPageSize; | ||
| 782 | else size = this._getpagesize(size); | ||
| 783 | |||
| 784 | if(orientation!=this.CurOrientation || size[0]!=this.CurPageSize[0] || size[1]!=this.CurPageSize[1]) | ||
| 785 | { | ||
| 786 | // New size or orientation | ||
| 787 | if(orientation=='P') { | ||
| 788 | this.w = size[0]; | ||
| 789 | this.h = size[1]; | ||
| 790 | } else { | ||
| 791 | this.w = size[1]; | ||
| 792 | this.h = size[0]; | ||
| 793 | } | ||
| 794 | this.wPt = this.w*this.k; | ||
| 795 | this.hPt = this.h*this.k; | ||
| 796 | this.CurOrientation = orientation; | ||
| 797 | this.CurPageSize = size; | ||
| 798 | } | ||
| 799 | if(orientation!=this.DefOrientation || size[0]!=this.DefPageSize[0] || size[1]!=this.DefPageSize[1]) | ||
| 800 | this.PageSizes[this.page] = [this.wPt, this.hPt]; | ||
| 801 | }; | ||
| 802 | |||
| 803 | RrdGfxPdf.prototype._endpage = function() | ||
| 804 | { | ||
| 805 | this.state = 1; | ||
| 806 | }; | ||
| 807 | |||
| 808 | RrdGfxPdf.prototype._escape = function(s) // FIXME | ||
| 809 | { | ||
| 810 | // Escape special characters in strings | ||
| 811 | //s = str_replace('\\','\\\\',s); | ||
| 812 | //s = str_replace('(','\\(',s); | ||
| 813 | //s = str_replace(')','\\)',s); | ||
| 814 | //s = str_replace("\r",'\\r',s); | ||
| 815 | return s.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); | ||
| 816 | }; | ||
| 817 | |||
| 818 | RrdGfxPdf.prototype._textstring = function(s) | ||
| 819 | { | ||
| 820 | // Format a text string | ||
| 821 | return '('+this._escape(s)+')'; | ||
| 822 | }; | ||
| 823 | |||
| 824 | RrdGfxPdf.prototype._newobj = function() | ||
| 825 | { | ||
| 826 | // Begin a new object | ||
| 827 | this.n++; | ||
| 828 | this.offsets[this.n] = this.buffer.length; | ||
| 829 | this._out(this.n+' 0 obj'); | ||
| 830 | }; | ||
| 831 | |||
| 832 | RrdGfxPdf.prototype._putstream = function(s) | ||
| 833 | { | ||
| 834 | this._out('stream'); | ||
| 835 | this._out(s); | ||
| 836 | this._out('endstream'); | ||
| 837 | }, | ||
| 838 | |||
| 839 | RrdGfxPdf.prototype._out = function(s) | ||
| 840 | { | ||
| 841 | // Add a line to the document | ||
| 842 | if(this.state==2) | ||
| 843 | this.pages[this.page] += s+"\n"; | ||
| 844 | else | ||
| 845 | this.buffer += s+"\n"; | ||
| 846 | }; | ||
| 847 | |||
| 848 | RrdGfxPdf.prototype._putpages = function() | ||
| 849 | { | ||
| 850 | var wPt, hPt; | ||
| 851 | var nb = this.page; | ||
| 852 | if(this.DefOrientation=='P') { | ||
| 853 | wPt = this.DefPageSize[0]*this.k; | ||
| 854 | hPt = this.DefPageSize[1]*this.k; | ||
| 855 | } else { | ||
| 856 | wPt = this.DefPageSize[1]*this.k; | ||
| 857 | hPt = this.DefPageSize[0]*this.k; | ||
| 858 | } | ||
| 859 | for(var n=1;n<=nb;n++) | ||
| 860 | { | ||
| 861 | // Page | ||
| 862 | this._newobj(); | ||
| 863 | this._out('<</Type /Page'); | ||
| 864 | this._out('/Parent 1 0 R'); | ||
| 865 | if(this.PageSizes[n] !== undefined) | ||
| 866 | this._out(sprintf('/MediaBox [0 0 %.2F %.2F]',this.PageSizes[n][0],this.PageSizes[n][1])); | ||
| 867 | this._out('/Resources 2 0 R'); | ||
| 868 | if(this.PDFVersion>'1.3') | ||
| 869 | this._out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>'); | ||
| 870 | this._out('/Contents '+(this.n+1)+' 0 R>>'); | ||
| 871 | this._out('endobj'); | ||
| 872 | // Page content | ||
| 873 | this._newobj(); | ||
| 874 | this._out('<</Length '+this.pages[n].length+'>>'); | ||
| 875 | this._putstream(this.pages[n]); | ||
| 876 | this._out('endobj'); | ||
| 877 | } | ||
| 878 | // Pages root | ||
| 879 | this.offsets[1] = this.buffer.length; | ||
| 880 | this._out('1 0 obj'); | ||
| 881 | this._out('<</Type /Pages'); | ||
| 882 | var kids = '/Kids ['; | ||
| 883 | for(var i=0;i<nb;i++) | ||
| 884 | kids += (3+2*i)+' 0 R '; | ||
| 885 | this._out(kids+']'); | ||
| 886 | this._out('/Count '+nb); | ||
| 887 | this._out(sprintf('/MediaBox [0 0 %.2F %.2F]',wPt,hPt)); | ||
| 888 | this._out('>>'); | ||
| 889 | this._out('endobj'); | ||
| 890 | }; | ||
| 891 | |||
| 892 | RrdGfxPdf.prototype._putfonts = function() | ||
| 893 | { | ||
| 894 | var nf = this.n; | ||
| 895 | for(var diff in this.diffs) { | ||
| 896 | // Encodings | ||
| 897 | this._newobj(); | ||
| 898 | this._out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['+diff+']>>'); | ||
| 899 | this._out('endobj'); | ||
| 900 | } | ||
| 901 | for(var font in this.fonts) { // FIXME | ||
| 902 | // Font objects | ||
| 903 | this.fonts[font]['n'] = this.n+1; | ||
| 904 | var name = this.fonts[font]['name']; | ||
| 905 | // Core font | ||
| 906 | this._newobj(); | ||
| 907 | this._out('<</Type /Font'); | ||
| 908 | this._out('/BaseFont /'+name); | ||
| 909 | this._out('/Subtype /Type1'); | ||
| 910 | if(name!='Symbol' && name!='ZapfDingbats') | ||
| 911 | this._out('/Encoding /WinAnsiEncoding'); | ||
| 912 | this._out('>>'); | ||
| 913 | this._out('endobj'); | ||
| 914 | } | ||
| 915 | }; | ||
| 916 | |||
| 917 | RrdGfxPdf.prototype._putresourcedict = function() | ||
| 918 | { | ||
| 919 | this._out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); | ||
| 920 | this._out('/Font <<'); | ||
| 921 | for(var font in this.fonts) | ||
| 922 | this._out('/F'+this.fonts[font]['i']+' '+this.fonts[font]['n']+' 0 R'); | ||
| 923 | this._out('>>'); | ||
| 924 | this._out('/XObject <<'); | ||
| 925 | this._out('>>'); | ||
| 926 | }; | ||
| 927 | |||
| 928 | RrdGfxPdf.prototype._putresources = function() | ||
| 929 | { | ||
| 930 | this._putfonts(); | ||
| 931 | // Resource dictionary | ||
| 932 | this.offsets[2] = this.buffer.length; | ||
| 933 | this._out('2 0 obj'); | ||
| 934 | this._out('<<'); | ||
| 935 | this._putresourcedict(); | ||
| 936 | this._out('>>'); | ||
| 937 | this._out('endobj'); | ||
| 938 | }; | ||
| 939 | |||
| 940 | RrdGfxPdf.prototype._putinfo = function() | ||
| 941 | { | ||
| 942 | // this._out('/Producer '+this._textstring('FPDF '+FPDF_VERSION)); FIXME | ||
| 943 | if(this.title != null) | ||
| 944 | this._out('/Title '+this._textstring(this.title)); | ||
| 945 | if(this.subject != null) | ||
| 946 | this._out('/Subject '+this._textstring(this.subject)); | ||
| 947 | if(this.author != null) | ||
| 948 | this._out('/Author '+this._textstring(this.author)); | ||
| 949 | if(this.keywords != null) | ||
| 950 | this._out('/Keywords '+this._textstring(this.keywords)); | ||
| 951 | if(this.creator != null) | ||
| 952 | this._out('/Creator '+this._textstring(this.creator)); | ||
| 953 | // this._out('/CreationDate '+this._textstring('D:'+date('YmdHis'))); // FIXME | ||
| 954 | }; | ||
| 955 | |||
| 956 | RrdGfxPdf.prototype._putcatalog = function() | ||
| 957 | { | ||
| 958 | this._out('/Type /Catalog'); | ||
| 959 | this._out('/Pages 1 0 R'); | ||
| 960 | |||
| 961 | if(this.ZoomMode=='fullpage') | ||
| 962 | this._out('/OpenAction [3 0 R /Fit]'); | ||
| 963 | else if(this.ZoomMode=='fullwidth') | ||
| 964 | this._out('/OpenAction [3 0 R /FitH null]'); | ||
| 965 | else if(this.ZoomMode=='real') | ||
| 966 | this._out('/OpenAction [3 0 R /XYZ null null 1]'); | ||
| 967 | else if(!(typeof this.ZoomMode === 'string')) | ||
| 968 | this._out('/OpenAction [3 0 R /XYZ null null '+sprintf('%.2F',this.ZoomMode/100)+']'); | ||
| 969 | |||
| 970 | if(this.LayoutMode=='single') | ||
| 971 | this._out('/PageLayout /SinglePage'); | ||
| 972 | else if(this.LayoutMode=='continuous') | ||
| 973 | this._out('/PageLayout /OneColumn'); | ||
| 974 | else if(this.LayoutMode=='two') | ||
| 975 | this._out('/PageLayout /TwoColumnLeft'); | ||
| 976 | }; | ||
| 977 | |||
| 978 | RrdGfxPdf.prototype._enddoc = function() | ||
| 979 | { | ||
| 980 | this._out('%PDF-'+this.PDFVersion); | ||
| 981 | this._putpages(); | ||
| 982 | this._putresources(); | ||
| 983 | // Info | ||
| 984 | this._newobj(); | ||
| 985 | this._out('<<'); | ||
| 986 | this._putinfo(); | ||
| 987 | this._out('>>'); | ||
| 988 | this._out('endobj'); | ||
| 989 | // Catalog | ||
| 990 | this._newobj(); | ||
| 991 | this._out('<<'); | ||
| 992 | this._putcatalog(); | ||
| 993 | this._out('>>'); | ||
| 994 | this._out('endobj'); | ||
| 995 | // Cross-ref | ||
| 996 | var o = this.buffer.length; | ||
| 997 | this._out('xref'); | ||
| 998 | this._out('0 '+(this.n+1)); | ||
| 999 | this._out('0000000000 65535 f '); | ||
| 1000 | for(var i=1;i<=this.n;i++) | ||
| 1001 | this._out(sprintf('%010d 00000 n ',this.offsets[i])); | ||
| 1002 | // Trailer | ||
| 1003 | this._out('trailer'); | ||
| 1004 | this._out('<<'); | ||
| 1005 | this._out('/Size '+(this.n+1)); | ||
| 1006 | this._out('/Root '+this.n+' 0 R'); | ||
| 1007 | this._out('/Info '+(this.n-1)+' 0 R'); | ||
| 1008 | this._out('>>'); | ||
| 1009 | this._out('startxref'); | ||
| 1010 | this._out(o); | ||
| 1011 | this._out('%%EOF'); | ||
| 1012 | this.state = 3; | ||
| 1013 | }; | ||
| 1014 | |||
diff --git a/js/RrdGfxSvg.js b/js/RrdGfxSvg.js new file mode 100644 index 0000000..2bdc1b9 --- /dev/null +++ b/js/RrdGfxSvg.js | |||
| @@ -0,0 +1,250 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * | ||
| 18 | * Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 19 | **/ | ||
| 20 | |||
| 21 | "use strict"; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * RrdGfxSvg | ||
| 25 | * @constructor | ||
| 26 | */ | ||
| 27 | var RrdGfxSvg = function(svgId) { | ||
| 28 | this.svg = document.getElementById(svgId) | ||
| 29 | this.svgns = "http://www.w3.org/2000/svg"; | ||
| 30 | this.xmlns = "http://www.w3.org/XML/1998/namespace"; | ||
| 31 | this.path = null; | ||
| 32 | this.path_color = null; | ||
| 33 | this.path_width = null; | ||
| 34 | }; | ||
| 35 | |||
| 36 | RrdGfxSvg.prototype.size = function (width, height) | ||
| 37 | { | ||
| 38 | while(this.svg.lastChild) | ||
| 39 | this.svg.removeChild(this.svg.lastChild); | ||
| 40 | |||
| 41 | this.svg.setAttribute("width", width+"px"); | ||
| 42 | this.svg.setAttribute("height", height+"px"); | ||
| 43 | this.svg.setAttribute("viewBox", "0 0 "+width+" "+height); | ||
| 44 | }; | ||
| 45 | |||
| 46 | RrdGfxSvg.prototype.set_dash = function (dashes, n, offset) | ||
| 47 | { | ||
| 48 | }; | ||
| 49 | |||
| 50 | RrdGfxSvg.prototype.line = function (X0, Y0, X1, Y1, width, color) | ||
| 51 | { | ||
| 52 | var shape = document.createElementNS(this.svgns, "line"); | ||
| 53 | |||
| 54 | X0 = Math.round(X0)+0.5; | ||
| 55 | Y0 = Math.round(Y0)+0.5; | ||
| 56 | X1 = Math.round(X1)+0.5; | ||
| 57 | Y1 = Math.round(Y1)+0.5; | ||
| 58 | |||
| 59 | shape.setAttributeNS(null, "x1", X0); | ||
| 60 | shape.setAttributeNS(null, "y1", Y0); | ||
| 61 | shape.setAttributeNS(null, "x2", X1); | ||
| 62 | shape.setAttributeNS(null, "y2", Y1); | ||
| 63 | shape.setAttributeNS(null, "stroke-width", width); | ||
| 64 | shape.setAttributeNS(null, "stroke", color); | ||
| 65 | |||
| 66 | this.svg.appendChild(shape); | ||
| 67 | }; | ||
| 68 | |||
| 69 | RrdGfxSvg.prototype.dashed_line = function (X0, Y0, X1, Y1, width, color, dash_on, dash_off) | ||
| 70 | { | ||
| 71 | var shape = document.createElementNS(this.svgns, "line"); | ||
| 72 | |||
| 73 | X0 = Math.round(X0)+0.5; | ||
| 74 | Y0 = Math.round(Y0)+0.5; | ||
| 75 | X1 = Math.round(X1)+0.5; | ||
| 76 | Y1 = Math.round(Y1)+0.5; | ||
| 77 | |||
| 78 | shape.setAttributeNS(null, "x1", X0); | ||
| 79 | shape.setAttributeNS(null, "y1", Y0); | ||
| 80 | shape.setAttributeNS(null, "x2", X1); | ||
| 81 | shape.setAttributeNS(null, "y2", Y1); | ||
| 82 | shape.setAttributeNS(null, "stroke-width", width); | ||
| 83 | shape.setAttributeNS(null, "stroke", color); | ||
| 84 | shape.setAttributeNS(null, "stroke-dasharray", dash_on+','+dash_off); | ||
| 85 | |||
| 86 | this.svg.appendChild(shape); | ||
| 87 | }; | ||
| 88 | |||
| 89 | RrdGfxSvg.prototype.rectangle = function (X0, Y0, X1, Y1, width, style) | ||
| 90 | { | ||
| 91 | var shape = document.createElementNS(this.svgns, "rect"); | ||
| 92 | |||
| 93 | var rwidth = Math.abs(X1-X0); | ||
| 94 | var rheight = Math.abs(Y1-Y0); | ||
| 95 | |||
| 96 | shape.setAttributeNS(null, "x", Math.round(X0)+0.5); | ||
| 97 | shape.setAttributeNS(null, "y", Math.round(Y0-rheight)+0.5); | ||
| 98 | shape.setAttributeNS(null, "width", rwidth); | ||
| 99 | shape.setAttributeNS(null, "height", rheight); | ||
| 100 | shape.setAttributeNS(null, "stroke-width", width); | ||
| 101 | shape.setAttributeNS(null, "stroke", style); | ||
| 102 | shape.setAttributeNS(null, "fill", "none"); | ||
| 103 | |||
| 104 | this.svg.appendChild(shape); | ||
| 105 | }; | ||
| 106 | |||
| 107 | RrdGfxSvg.prototype.new_area = function (X0, Y0, X1, Y1, X2, Y2, color) | ||
| 108 | { | ||
| 109 | X0 = Math.round(X0)+0.5; | ||
| 110 | Y0 = Math.round(Y0)+0.5; | ||
| 111 | X1 = Math.round(X1)+0.5; | ||
| 112 | Y1 = Math.round(Y1)+0.5; | ||
| 113 | X2 = Math.round(X2)+0.5; | ||
| 114 | Y2 = Math.round(Y2)+0.5; | ||
| 115 | |||
| 116 | this.path_color = color; | ||
| 117 | this.path = 'M'+X0+','+Y0; | ||
| 118 | this.path += ' L'+X1+','+Y1; | ||
| 119 | this.path += ' L'+X2+','+Y2; | ||
| 120 | }; | ||
| 121 | |||
| 122 | RrdGfxSvg.prototype.add_point = function (x, y) | ||
| 123 | { | ||
| 124 | x = Math.round(x)+0.5; | ||
| 125 | y = Math.round(y)+0.5; | ||
| 126 | |||
| 127 | this.path += ' L'+x+','+y; | ||
| 128 | }; | ||
| 129 | |||
| 130 | RrdGfxSvg.prototype.close_path = function () | ||
| 131 | { | ||
| 132 | var shape = document.createElementNS(this.svgns, "path"); | ||
| 133 | |||
| 134 | this.path += ' Z'; | ||
| 135 | |||
| 136 | shape.setAttributeNS(null, "d", this.path); | ||
| 137 | shape.setAttributeNS(null, "fill", this.path_color); | ||
| 138 | shape.setAttributeNS(null, "stroke", 'none'); | ||
| 139 | |||
| 140 | this.svg.appendChild(shape); | ||
| 141 | }; | ||
| 142 | |||
| 143 | RrdGfxSvg.prototype.stroke_begin = function (width, style) | ||
| 144 | { | ||
| 145 | this.path_width = width; | ||
| 146 | this.path_color = style; | ||
| 147 | this.path = ''; | ||
| 148 | }; | ||
| 149 | |||
| 150 | RrdGfxSvg.prototype.stroke_end = function () | ||
| 151 | { | ||
| 152 | var shape = document.createElementNS(this.svgns, "path"); | ||
| 153 | |||
| 154 | shape.setAttributeNS(null, "d", this.path); | ||
| 155 | shape.setAttributeNS(null, "fill", 'none'); | ||
| 156 | shape.setAttributeNS(null, "stroke", this.path_color); | ||
| 157 | shape.setAttributeNS(null, "stroke-width", this.path_width); | ||
| 158 | shape.setAttributeNS(null, "stroke-linecap", 'round'); | ||
| 159 | shape.setAttributeNS(null, "stroke-linejoin", 'round'); | ||
| 160 | |||
| 161 | this.svg.appendChild(shape); | ||
| 162 | }; | ||
| 163 | |||
| 164 | RrdGfxSvg.prototype.moveTo = function (x,y) | ||
| 165 | { | ||
| 166 | x = Math.round(x)+0.5; | ||
| 167 | y = Math.round(y)+0.5; | ||
| 168 | |||
| 169 | this.path += ' M'+x+','+y; | ||
| 170 | }; | ||
| 171 | |||
| 172 | RrdGfxSvg.prototype.lineTo = function (x,y) | ||
| 173 | { | ||
| 174 | x = Math.round(x)+0.5; | ||
| 175 | y = Math.round(y)+0.5; | ||
| 176 | |||
| 177 | this.path += ' L'+x+','+y; | ||
| 178 | }; | ||
| 179 | |||
| 180 | RrdGfxSvg.prototype.text = function (x, y, color, font, tabwidth, angle, h_align, v_align, text) | ||
| 181 | { | ||
| 182 | x = Math.round(x); | ||
| 183 | y = Math.round(y); | ||
| 184 | |||
| 185 | var svgtext = document.createElementNS(this.svgns, "text"); | ||
| 186 | |||
| 187 | var data = document.createTextNode(text); | ||
| 188 | |||
| 189 | svgtext.setAttributeNS(null, "x", x); | ||
| 190 | svgtext.setAttributeNS(null, "y", y); | ||
| 191 | svgtext.setAttributeNS(null, "fill", color); | ||
| 192 | svgtext.setAttributeNS(null, "stroke", "none"); | ||
| 193 | svgtext.setAttributeNS(null, "font-family", font.font); | ||
| 194 | svgtext.setAttributeNS(null, "font-size", font.size+"px"); | ||
| 195 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve"); | ||
| 196 | |||
| 197 | angle=-angle; | ||
| 198 | svgtext.setAttributeNS(null, "transform", 'rotate('+angle+' '+x+','+y+')' ); | ||
| 199 | |||
| 200 | switch (h_align) { | ||
| 201 | case RrdGraph.GFX_H_LEFT: | ||
| 202 | svgtext.setAttributeNS(null, "text-anchor", 'start'); | ||
| 203 | break; | ||
| 204 | case RrdGraph.GFX_H_RIGHT: | ||
| 205 | svgtext.setAttributeNS(null, "text-anchor", 'end'); | ||
| 206 | break; | ||
| 207 | case RrdGraph.GFX_H_CENTER: | ||
| 208 | svgtext.setAttributeNS(null, "text-anchor", 'middle'); | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | svgtext.appendChild(data); | ||
| 212 | this.svg.appendChild(svgtext); | ||
| 213 | |||
| 214 | var bbox = svgtext.getBBox(); | ||
| 215 | |||
| 216 | switch (v_align) { // FIXME | ||
| 217 | case RrdGraph.GFX_V_TOP: | ||
| 218 | svgtext.setAttributeNS(null, "y", y+bbox.height/2); | ||
| 219 | break; | ||
| 220 | case RrdGraph.GFX_V_BOTTOM: | ||
| 221 | svgtext.setAttributeNS(null, "y", y-bbox.height/6); | ||
| 222 | break; | ||
| 223 | case RrdGraph.GFX_V_CENTER: | ||
| 224 | svgtext.setAttributeNS(null, "y", y+bbox.height/4); | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | }; | ||
| 228 | |||
| 229 | RrdGfxSvg.prototype.get_text_width = function(start, font, tabwidth, text) | ||
| 230 | { | ||
| 231 | var svgtext = document.createElementNS(this.svgns, "text"); | ||
| 232 | var data = document.createTextNode(text); | ||
| 233 | svgtext.setAttributeNS(null, "x", 0); | ||
| 234 | svgtext.setAttributeNS(null, "y", 0); | ||
| 235 | svgtext.setAttributeNS(null, "fill", 'none'); | ||
| 236 | svgtext.setAttributeNS(null, "stroke", 'none'); | ||
| 237 | svgtext.setAttributeNS(null, "font-family", font.font); | ||
| 238 | svgtext.setAttributeNS(null, "font-size", font.size+"px"); | ||
| 239 | svgtext.setAttributeNS(this.xmlns, "xml:space", "preserve"); | ||
| 240 | svgtext.appendChild(data); | ||
| 241 | this.svg.appendChild(svgtext); | ||
| 242 | |||
| 243 | var bbox = svgtext.getBBox(); | ||
| 244 | |||
| 245 | svgtext.removeChild(data); | ||
| 246 | this.svg.removeChild(svgtext); | ||
| 247 | |||
| 248 | return bbox.width; | ||
| 249 | }; | ||
| 250 | |||
diff --git a/js/RrdGraph.js b/js/RrdGraph.js new file mode 100644 index 0000000..9b8e3db --- /dev/null +++ b/js/RrdGraph.js | |||
| @@ -0,0 +1,2914 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010 | ||
| 18 | * | ||
| 19 | * Convert to javascript: Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 20 | **/ | ||
| 21 | |||
| 22 | "use strict"; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * RrdGraphDescError | ||
| 26 | * @constructor | ||
| 27 | */ | ||
| 28 | var RrdGraphDescError = function (message) | ||
| 29 | { | ||
| 30 | this.prototype = Error.prototype; | ||
| 31 | this.name = "RrdGraphDescError"; | ||
| 32 | this.message = (message) ? message : "Error"; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * RrdGraphDesc | ||
| 37 | * @constructor | ||
| 38 | */ | ||
| 39 | var RrdGraphDesc = function (graph) | ||
| 40 | { | ||
| 41 | this.gf = null; /* graphing function */ | ||
| 42 | this.stack = false; /* boolean */ | ||
| 43 | this.debug = false; /* boolean */ | ||
| 44 | this.vname = null; /* name of the variable */ | ||
| 45 | this.vidx = Number.NaN; /* gdes reference */ | ||
| 46 | this.rrd = null; /* name of the rrd_file containing data */ | ||
| 47 | this.ds_nam = null; /* data source name */ | ||
| 48 | this.ds = -1; /* data source number */ | ||
| 49 | this.cf = RrdGraphDesc.CF_AVERAGE; /* consolidation function */ | ||
| 50 | this.cf_reduce = RrdGraphDesc.CF_AVERAGE; /* consolidation function for reduce_data() */ | ||
| 51 | this.col = null; /* graph color */ | ||
| 52 | this.format = null; /* format for PRINT AND GPRINT */ | ||
| 53 | this.legend = null; /* legend */ | ||
| 54 | this.strftm = false; /* should the VDEF legend be formated with strftime */ | ||
| 55 | this.leg_x = 0; /* location of legend */ | ||
| 56 | this.leg_y = 0; | ||
| 57 | this.yrule = Number.NaN; /* value for y rule line and for VDEF */ | ||
| 58 | this.xrule = 0; /* time for x rule line and for VDEF */ | ||
| 59 | this.vf = null; /* instruction for VDEF function */ | ||
| 60 | this.rpnp = null; /* instructions for CDEF function */ | ||
| 61 | |||
| 62 | /* SHIFT implementation */ | ||
| 63 | this.shidx = 0; /* gdes reference for offset (-1 --> constant) */ | ||
| 64 | this.shval = 0; /* offset if shidx is -1 */ | ||
| 65 | this.shift = 0; /* current shift applied */ | ||
| 66 | |||
| 67 | /* description of data fetched for the graph element */ | ||
| 68 | this.start = graph.start; /* timestaps for first and last data element */ | ||
| 69 | this.end = graph.end; | ||
| 70 | this.start_orig = graph.start; /* timestaps for first and last data element */ | ||
| 71 | this.end_orig = graph.end; | ||
| 72 | this.step = graph.step; /* time between samples */ | ||
| 73 | this.step_orig = graph.step; /* time between samples */ | ||
| 74 | this.ds_cnt = 0; /* how many data sources are there in the fetch */ | ||
| 75 | this.data_first = 0; /* first pointer to this data */ | ||
| 76 | this.ds_namv = []; /* name of datasources in the fetch. */ | ||
| 77 | this.data = []; /* the raw data drawn from the rrd */ | ||
| 78 | this.p_data = []; /* processed data, xsize elments */ | ||
| 79 | this.linewidth = 0; /* linewideth */ | ||
| 80 | |||
| 81 | /* dashed line stuff */ | ||
| 82 | this.dash = false; /* boolean, draw dashed line? */ | ||
| 83 | this.p_dashes = []; /* pointer do dash array which keeps the lengths of dashes */ | ||
| 84 | this.ndash = false; /* number of dash segments */ | ||
| 85 | this.offset = 0; /* dash offset along the line */ | ||
| 86 | |||
| 87 | this.txtalign = 0; /* change default alignment strategy for text */ | ||
| 88 | |||
| 89 | /** ** **/ | ||
| 90 | var args = []; // FIXME other way ¿? | ||
| 91 | var type = arguments[1] | ||
| 92 | args[0] = arguments[0]; | ||
| 93 | for(var i = 2; i < arguments.length; i++) args[i-1] = arguments[i]; | ||
| 94 | |||
| 95 | switch (type) { | ||
| 96 | case RrdGraphDesc.GF_GPRINT: | ||
| 97 | this.gprint.apply(this, args); | ||
| 98 | break; | ||
| 99 | case RrdGraphDesc.GF_COMMENT: | ||
| 100 | this.comment.apply(this, args); | ||
| 101 | break; | ||
| 102 | case RrdGraphDesc.GF_HRULE: | ||
| 103 | this.hrule.apply(this, args); | ||
| 104 | break; | ||
| 105 | case RrdGraphDesc.GF_VRULE: | ||
| 106 | this.vrule.apply(this, args); | ||
| 107 | break; | ||
| 108 | case RrdGraphDesc.GF_LINE: | ||
| 109 | this.line.apply(this, args); | ||
| 110 | break; | ||
| 111 | case RrdGraphDesc.GF_AREA: | ||
| 112 | this.area.apply(this, args); | ||
| 113 | break; | ||
| 114 | case RrdGraphDesc.GF_TICK: | ||
| 115 | this.tick.apply(this, args); | ||
| 116 | break; | ||
| 117 | case RrdGraphDesc.GF_TEXTALIGN: | ||
| 118 | this.textaling.apply(this, args); | ||
| 119 | break; | ||
| 120 | case RrdGraphDesc.GF_DEF: | ||
| 121 | this.def.apply(this, args); | ||
| 122 | break; | ||
| 123 | case RrdGraphDesc.GF_CDEF: | ||
| 124 | this.cdef.apply(this, args); | ||
| 125 | break; | ||
| 126 | case RrdGraphDesc.GF_VDEF: | ||
| 127 | this.vdef.apply(this, args); | ||
| 128 | break; | ||
| 129 | case RrdGraphDesc.GF_SHIFT: | ||
| 130 | this.fshift.apply(this, args); | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | RrdGraphDesc.GF_PRINT = 0; | ||
| 136 | RrdGraphDesc.GF_GPRINT = 1; | ||
| 137 | RrdGraphDesc.GF_COMMENT = 2; | ||
| 138 | RrdGraphDesc.GF_HRULE = 3; | ||
| 139 | RrdGraphDesc.GF_VRULE = 4; | ||
| 140 | RrdGraphDesc.GF_LINE = 5; | ||
| 141 | RrdGraphDesc.GF_AREA = 6; | ||
| 142 | RrdGraphDesc.GF_STACK = 7; | ||
| 143 | RrdGraphDesc.GF_TICK = 8; | ||
| 144 | RrdGraphDesc.GF_TEXTALIGN = 9; | ||
| 145 | RrdGraphDesc.GF_DEF = 10; | ||
| 146 | RrdGraphDesc.GF_CDEF = 11; | ||
| 147 | RrdGraphDesc.GF_VDEF = 12; | ||
| 148 | RrdGraphDesc.GF_SHIFT = 13; | ||
| 149 | RrdGraphDesc.GF_XPORT = 14; | ||
| 150 | |||
| 151 | RrdGraphDesc.CF_AVERAGE = 0; | ||
| 152 | RrdGraphDesc.CF_MINIMUM = 1; | ||
| 153 | RrdGraphDesc.CF_MAXIMUM = 2; | ||
| 154 | RrdGraphDesc.CF_LAST = 3; | ||
| 155 | RrdGraphDesc.CF_HWPREDICT = 4; | ||
| 156 | RrdGraphDesc.CF_SEASONAL = 5; | ||
| 157 | RrdGraphDesc.CF_DEVPREDICT = 6; | ||
| 158 | RrdGraphDesc.CF_DEVSEASONAL = 7; | ||
| 159 | RrdGraphDesc.CF_FAILURES = 8; | ||
| 160 | RrdGraphDesc.CF_MHWPREDICT = 9; | ||
| 161 | |||
| 162 | RrdGraphDesc.TXA_LEFT = 0; | ||
| 163 | RrdGraphDesc.TXA_RIGHT = 1; | ||
| 164 | RrdGraphDesc.TXA_CENTER = 2; | ||
| 165 | RrdGraphDesc.TXA_JUSTIFIED = 3; | ||
| 166 | |||
| 167 | RrdGraphDesc.cf_conv = function (str) | ||
| 168 | { | ||
| 169 | switch (str){ | ||
| 170 | case 'AVERAGE': return RrdGraphDesc.CF_AVERAGE; | ||
| 171 | case 'MIN': return RrdGraphDesc.CF_MINIMUM; | ||
| 172 | case 'MAX': return RrdGraphDesc.CF_MAXIMUM; | ||
| 173 | case 'LAST': return RrdGraphDesc.CF_LAST; | ||
| 174 | case 'HWPREDICT': return RrdGraphDesc.CF_HWPREDICT; | ||
| 175 | case 'MHWPREDICT': return RrdGraphDesc.CF_MHWPREDICT; | ||
| 176 | case 'DEVPREDICT': return RrdGraphDesc.CF_DEVPREDICT; | ||
| 177 | case 'SEASONAL': return RrdGraphDesc.CF_SEASONAL; | ||
| 178 | case 'DEVSEASONAL': return RrdGraphDesc.CF_DEVSEASONAL; | ||
| 179 | case 'FAILURES': return RrdGraphDesc.CF_FAILURES; | ||
| 180 | } | ||
| 181 | return -1; | ||
| 182 | }; | ||
| 183 | |||
| 184 | RrdGraphDesc.cf2str = function (cf) | ||
| 185 | { | ||
| 186 | switch (cf){ | ||
| 187 | case RrdGraphDesc.CF_AVERAGE: return 'AVERAGE'; | ||
| 188 | case RrdGraphDesc.CF_MINIMUM: return 'MIN'; | ||
| 189 | case RrdGraphDesc.CF_MAXIMUM: return 'MAX'; | ||
| 190 | case RrdGraphDesc.CF_LAST: return 'LAST'; | ||
| 191 | case RrdGraphDesc.CF_HWPREDICT: return 'HWPREDICT'; | ||
| 192 | case RrdGraphDesc.CF_MHWPREDICT: return 'MHWPREDICT'; | ||
| 193 | case RrdGraphDesc.CF_DEVPREDICT: return 'DEVPREDICT'; | ||
| 194 | case RrdGraphDesc.CF_SEASONAL: return 'SEASONAL'; | ||
| 195 | case RrdGraphDesc.CF_DEVSEASONAL: return 'DEVSEASONAL'; | ||
| 196 | case RrdGraphDesc.CF_FAILURES: return 'FAILURES'; | ||
| 197 | } | ||
| 198 | return ''; | ||
| 199 | }; | ||
| 200 | |||
| 201 | RrdGraphDesc.prototype.def = function (graph, vname, rrdfile, name, cf, step, start, end, reduce) | ||
| 202 | { | ||
| 203 | var start_t = new RrdTime(this.start); | ||
| 204 | var end_t = new RrdTime(this.end); | ||
| 205 | |||
| 206 | this.gf = RrdGraphDesc.GF_DEF; | ||
| 207 | this.vname = vname; | ||
| 208 | this.vidx = graph.find_var(vname); | ||
| 209 | this.rrd = rrdfile; | ||
| 210 | this.ds_nam = name; | ||
| 211 | this.cf = RrdGraphDesc.cf_conv(cf); | ||
| 212 | |||
| 213 | if (step != undefined && step != null) | ||
| 214 | this.step = step; | ||
| 215 | if (start != undefined && start != null) | ||
| 216 | start_t = new RrdTime(start); | ||
| 217 | if (end != undefined && end != null) | ||
| 218 | end_t = new RrdTime(end); | ||
| 219 | if (reduce === undefined || reduce === null) | ||
| 220 | this.cf_reduce = this.cf; // ¿? | ||
| 221 | else | ||
| 222 | this.cf_reduce = RrdGraphDesc.cf_conv(reduce); | ||
| 223 | this.legend = ''; | ||
| 224 | |||
| 225 | var start_end = RrdTime.proc_start_end(start_t, end_t); // FIXME here? | ||
| 226 | this.start = start_end[0]; | ||
| 227 | this.end = start_end[1]; | ||
| 228 | this.start_orig = start_end[0]; | ||
| 229 | this.end_orig = start_end[1]; | ||
| 230 | }; | ||
| 231 | |||
| 232 | RrdGraphDesc.prototype.cdef = function (graph, vname, rpn) | ||
| 233 | { | ||
| 234 | this.gf = RrdGraphDesc.GF_CDEF; | ||
| 235 | this.vname = vname; | ||
| 236 | this.vidx = graph.find_var(vname); | ||
| 237 | this.rpnp = new RrdRpn(rpn, graph.gdes); | ||
| 238 | this.legend = ''; | ||
| 239 | }; | ||
| 240 | |||
| 241 | RrdGraphDesc.prototype.vdef = function (graph, vname, rpn) | ||
| 242 | { | ||
| 243 | this.gf = RrdGraphDesc.GF_VDEF; | ||
| 244 | this.vname = vname; | ||
| 245 | |||
| 246 | var index = rpn.indexOf(','); | ||
| 247 | var name = rpn.substring(0,index); | ||
| 248 | this.vidx = graph.find_var(name); // FIXME checks | ||
| 249 | if (graph.gdes[this.vidx].gf != RrdGraphDesc.GF_DEF && graph.gdes[this.vidx].gf != RrdGraphDesc.GF_CDEF) { | ||
| 250 | throw new RrdGraphDescError('variable "'+name+'" not DEF nor CDEF in VDEF.'); | ||
| 251 | } | ||
| 252 | this.vf = new RrdVdef(rpn.substring(index+1)); | ||
| 253 | this.legend = ''; | ||
| 254 | }; | ||
| 255 | |||
| 256 | RrdGraphDesc.prototype.fshift = function (graph, vname, offset) | ||
| 257 | { | ||
| 258 | this.gf = RrdGraphDesc.GF_SHIFT; | ||
| 259 | this.vname = vname; // ¿? | ||
| 260 | this.vidx = graph.find_var(vname); // FIXME checks | ||
| 261 | |||
| 262 | if (graph.gdes[this.vidx].gf === RrdGraphDesc.GF_VDEF) | ||
| 263 | throw new RrdGraphDescError("Cannot shift a VDEF: '%s' in line '"+graph.gdes[this.vidx].vname+"'"); | ||
| 264 | if (graph.gdes[this.vidx].gf !== RrdGraphDesc.GF_DEF && graph.gdes[this.vidx].gf !== RrdGraphDesc.GF_CDEF) | ||
| 265 | throw new RrdGraphDescError("Encountered unknown type variable '"+graph.gdes[this.vidx].vname+"'"); | ||
| 266 | |||
| 267 | this.shidx = graph.find_var(offset); | ||
| 268 | if (this.shidx >= 0) { | ||
| 269 | if (graph.gdes[gdp.shidx].gf === RrdGraphDesc.GF_DEF || graph.gdes[gdp.shidx].gf === RrdGraphDesc.GF_CDEF) | ||
| 270 | throw new RrdGraphDescError("Offset cannot be a (C)DEF: '"+graph.gdes[gdp.shidx].gf+"'"); | ||
| 271 | if (graph.gdes[gdp.shidx].gf !== RrdGraphDesc.GF_VDEF) | ||
| 272 | throw new RrdGraphDescError("Encountered unknown type variable '"+graph.gdes[gdp.shidx].vname+"'"); | ||
| 273 | } else { | ||
| 274 | this.shval = parseInt(offset, 10); // FIXME check | ||
| 275 | this.shidx = -1; | ||
| 276 | } | ||
| 277 | this.legend = ''; | ||
| 278 | }; | ||
| 279 | |||
| 280 | RrdGraphDesc.prototype.line = function (graph, width, value, color, legend, stack) | ||
| 281 | { | ||
| 282 | this.gf = RrdGraphDesc.GF_LINE; | ||
| 283 | this.vname = value; | ||
| 284 | this.vidx = graph.find_var(value); | ||
| 285 | this.linewidth = width; | ||
| 286 | this.col = color; | ||
| 287 | if (legend === undefined) this.legend = ''; | ||
| 288 | else this.legend = ' '+legend; | ||
| 289 | if (stack === undefined) this.stack = false; | ||
| 290 | else this.stack = stack; | ||
| 291 | this.format = this.legend; | ||
| 292 | }; | ||
| 293 | |||
| 294 | RrdGraphDesc.prototype.area = function (graph, value, color, legend, stack) | ||
| 295 | { | ||
| 296 | this.gf = RrdGraphDesc.GF_AREA; | ||
| 297 | this.vname = value; | ||
| 298 | this.vidx = graph.find_var(value); | ||
| 299 | this.col = color; | ||
| 300 | if (legend === undefined) this.legend = ''; | ||
| 301 | else this.legend = ' '+legend; | ||
| 302 | if (stack === undefined) this.stack = false; | ||
| 303 | else this.stack = stack; | ||
| 304 | this.format = this.legend; | ||
| 305 | }; | ||
| 306 | |||
| 307 | RrdGraphDesc.prototype.tick = function (graph, vname, color, fraction, legend) | ||
| 308 | { | ||
| 309 | this.gf = RrdGraphDesc.GF_TICK; | ||
| 310 | this.vname = vname; | ||
| 311 | this.vidx = graph.find_var(vname); | ||
| 312 | this.col = color; | ||
| 313 | if (fraction !== undefined) | ||
| 314 | this.yrule = fraction; | ||
| 315 | if (legend === undefined) this.legend = ''; | ||
| 316 | else this.legend = ' '+legend; | ||
| 317 | this.format = this.legend; | ||
| 318 | }; | ||
| 319 | |||
| 320 | RrdGraphDesc.prototype.gprint = function (graph, vname, cf, format, strftimefmt) | ||
| 321 | { | ||
| 322 | this.gf = RrdGraphDesc.GF_GPRINT; | ||
| 323 | this.vname = vname; | ||
| 324 | this.vidx = graph.find_var(vname); | ||
| 325 | this.legend = ''; | ||
| 326 | if (format === undefined) { | ||
| 327 | this.format = cf; | ||
| 328 | switch (graph.gdes[this.vidx].gf) { | ||
| 329 | case RrdGraphDesc.GF_DEF: | ||
| 330 | case RrdGraphDesc.GF_CDEF: | ||
| 331 | this.cf = graph.gdes[this.vidx].cf; | ||
| 332 | break; | ||
| 333 | case RrdGraphDesc.GF_VDEF: | ||
| 334 | break; | ||
| 335 | default: | ||
| 336 | throw new RrdGraphDescError("Encountered unknown type variable "+graph.gdes[this.vidx].vname); | ||
| 337 | } | ||
| 338 | } else { | ||
| 339 | this.cf = RrdGraphDesc.cf_conv(cf); | ||
| 340 | this.format = format; | ||
| 341 | } | ||
| 342 | if (graph.gdes[this.vidx].gf === RrdGraphDesc.GF_VDEF && strftimefmt === true) | ||
| 343 | this.strftm = true; | ||
| 344 | }; | ||
| 345 | |||
| 346 | RrdGraphDesc.prototype.comment = function (graph, text) | ||
| 347 | { | ||
| 348 | this.gf = RrdGraphDesc.GF_COMMENT; | ||
| 349 | this.vidx = -1; | ||
| 350 | this.legend = text; | ||
| 351 | }; | ||
| 352 | |||
| 353 | RrdGraphDesc.prototype.textalign = function (graph, align) | ||
| 354 | { | ||
| 355 | this.gf = RrdGraphDesc.GF_TEXTALIGN; | ||
| 356 | this.vidx = -1; | ||
| 357 | if (align === "left") { | ||
| 358 | this.txtalign = RrdGraphDesc.TXA_LEFT; | ||
| 359 | } else if (align === "right") { | ||
| 360 | this.txtalign = RrdGraphDesc.TXA_RIGHT; | ||
| 361 | } else if (align === "justified") { | ||
| 362 | this.txtalign = RrdGraphDesc.TXA_JUSTIFIED; | ||
| 363 | } else if (align === "center") { | ||
| 364 | this.txtalign = RrdGraphDesc.TXA_CENTER; | ||
| 365 | } else { | ||
| 366 | throw new RrdGraphDescError("Unknown alignement type '"+align+"'"); | ||
| 367 | } | ||
| 368 | }; | ||
| 369 | |||
| 370 | RrdGraphDesc.prototype.vrule = function (graph, time, color, legend) | ||
| 371 | { | ||
| 372 | this.gf = RrdGraphDesc.GF_VRULE; | ||
| 373 | this.xrule = time; | ||
| 374 | this.col = color; | ||
| 375 | if (legend === undefined) this.legend = ''; | ||
| 376 | else this.legend = ' '+legend; | ||
| 377 | }; | ||
| 378 | |||
| 379 | RrdGraphDesc.prototype.hrule = function (graph, value, color, legend) | ||
| 380 | { | ||
| 381 | this.gf = RrdGraphDesc.GF_HRULE; | ||
| 382 | this.yrule = value; | ||
| 383 | this.col = color; | ||
| 384 | if (legend === undefined) this.legend = ''; | ||
| 385 | else this.legend = ' '+legend; | ||
| 386 | }; | ||
| 387 | |||
| 388 | /** | ||
| 389 | * RrdVdefError | ||
| 390 | * @constructor | ||
| 391 | */ | ||
| 392 | var RrdVdefError = function (message) | ||
| 393 | { | ||
| 394 | this.prototype = Error.prototype; | ||
| 395 | this.name = "RrdVdefError"; | ||
| 396 | this.message = (message) ? message : "Error"; | ||
| 397 | }; | ||
| 398 | |||
| 399 | /** | ||
| 400 | * RrdVdef | ||
| 401 | * @constructor | ||
| 402 | */ | ||
| 403 | var RrdVdef = function(vname, str) /* parse */ | ||
| 404 | { | ||
| 405 | var param; | ||
| 406 | var func; | ||
| 407 | var n = 0; | ||
| 408 | |||
| 409 | this.expr = str; | ||
| 410 | this.op = null; | ||
| 411 | this.param = null; | ||
| 412 | this.val = null; | ||
| 413 | this.when = null; | ||
| 414 | |||
| 415 | var index = str.indexOf(','); | ||
| 416 | if (index != -1) { | ||
| 417 | param = parseFloat(str.substr(0,index)); | ||
| 418 | func = str.substr(index+1); | ||
| 419 | } else { | ||
| 420 | param = Number.NaN; | ||
| 421 | func = str; | ||
| 422 | } | ||
| 423 | |||
| 424 | if (func === 'PERCENT') this.op = RrdVdef.VDEF_PERCENT; | ||
| 425 | else if (func === 'PERCENTNAN') this.op = RrdVdef.VDEF_PERCENTNAN; | ||
| 426 | else if (func === 'MAXIMUM') this.op = RrdVdef.VDEF_MAXIMUM; | ||
| 427 | else if (func === 'AVERAGE') this.op = RrdVdef.VDEF_AVERAGE; | ||
| 428 | else if (func === 'STDEV') this.op = RrdVdef.VDEF_STDEV; | ||
| 429 | else if (func === 'MINIMUM') this.op = RrdVdef.VDEF_MINIMUM; | ||
| 430 | else if (func === 'TOTAL') this.op = RrdVdef.VDEF_TOTAL; | ||
| 431 | else if (func === 'FIRST') this.op = RrdVdef.VDEF_FIRST; | ||
| 432 | else if (func === 'LAST') this.op = RrdVdef.VDEF_LAST; | ||
| 433 | else if (func === 'LSLSLOPE') this.op = RrdVdef.VDEF_LSLSLOPE; | ||
| 434 | else if (func === 'LSLINT') this.op = RrdVdef.VDEF_LSLINT; | ||
| 435 | else if (func === 'LSLCORREL') this.op = RrdVdef.VDEF_LSLCORREL; | ||
| 436 | else { | ||
| 437 | throw new RrdVdefError('Unknown function "'+func+'" in VDEF "'+vame+'"'); | ||
| 438 | } | ||
| 439 | |||
| 440 | switch (this.op) { | ||
| 441 | case RrdVdef.VDEF_PERCENT: | ||
| 442 | case RrdVdef.VDEF_PERCENTNAN: | ||
| 443 | if (isNaN(param)) { /* no parameter given */ | ||
| 444 | throw new RrdVdefError("Function '"+func+"' needs parameter in VDEF '"+vname+"'"); | ||
| 445 | } | ||
| 446 | if (param >= 0.0 && param <= 100.0) { | ||
| 447 | this.param = param; | ||
| 448 | this.val = Number.NaN; /* undefined */ | ||
| 449 | this.when = 0; /* undefined */ | ||
| 450 | } else { | ||
| 451 | throw new RrdVdefError("Parameter '"+param+"' out of range in VDEF '"+vname+"'"); | ||
| 452 | } | ||
| 453 | break; | ||
| 454 | case RrdVdef.VDEF_MAXIMUM: | ||
| 455 | case RrdVdef.VDEF_AVERAGE: | ||
| 456 | case RrdVdef.VDEF_STDEV: | ||
| 457 | case RrdVdef.VDEF_MINIMUM: | ||
| 458 | case RrdVdef.VDEF_TOTAL: | ||
| 459 | case RrdVdef.VDEF_FIRST: | ||
| 460 | case RrdVdef.VDEF_LAST: | ||
| 461 | case RrdVdef.VDEF_LSLSLOPE: | ||
| 462 | case RrdVdef.VDEF_LSLINT: | ||
| 463 | case RrdVdef.VDEF_LSLCORREL: | ||
| 464 | if (isNaN(param)) { | ||
| 465 | this.param = Number.NaN; | ||
| 466 | this.val = Number.NaN; | ||
| 467 | this.when = 0; | ||
| 468 | } else { | ||
| 469 | throw new RrdVdefError("Function '"+func+"' needs no parameter in VDEF '"+vname+"'"); | ||
| 470 | } | ||
| 471 | break; | ||
| 472 | } | ||
| 473 | }; | ||
| 474 | |||
| 475 | RrdVdef.VDEF_MAXIMUM = 0; | ||
| 476 | RrdVdef.VDEF_MINIMUM = 1; | ||
| 477 | RrdVdef.VDEF_AVERAGE = 2; | ||
| 478 | RrdVdef.VDEF_STDEV = 3; | ||
| 479 | RrdVdef.VDEF_PERCENT = 4; | ||
| 480 | RrdVdef.VDEF_TOTAL = 5; | ||
| 481 | RrdVdef.VDEF_FIRST = 6; | ||
| 482 | RrdVdef.VDEF_LAST = 7; | ||
| 483 | RrdVdef.VDEF_LSLSLOPE = 8; | ||
| 484 | RrdVdef.VDEF_LSLINT = 9; | ||
| 485 | RrdVdef.VDEF_LSLCORREL = 10; | ||
| 486 | RrdVdef.VDEF_PERCENTNAN = 11; | ||
| 487 | |||
| 488 | RrdVdef.prototype.vdef_percent_compar = function (a, b) | ||
| 489 | { /* Equality is not returned; this doesn't hurt except (maybe) for a little performance. */ | ||
| 490 | /* NaN < -INF < finite_values < INF */ | ||
| 491 | if (isNaN(a)) return -1; | ||
| 492 | if (isNaN(b)) return 1; | ||
| 493 | /* NaN doesn't reach this part so INF and -INF are extremes. The sign from isinf() is compatible with the sign we return */ | ||
| 494 | if (!isFinite(a)) { | ||
| 495 | if (a === -Infinity) return -1; | ||
| 496 | else return 1; | ||
| 497 | } | ||
| 498 | if (!isFinite(b)) { | ||
| 499 | if (b === -Infinity) return -1; | ||
| 500 | else return 1; | ||
| 501 | } | ||
| 502 | /* If we reach this, both values must be finite */ | ||
| 503 | if (a < b) return -1; | ||
| 504 | else return 1; | ||
| 505 | }; | ||
| 506 | |||
| 507 | RrdVdef.prototype.calc = function(src) | ||
| 508 | { | ||
| 509 | var data; | ||
| 510 | var step, steps; | ||
| 511 | |||
| 512 | data = src.data; | ||
| 513 | steps = (src.end - src.start) / src.step; | ||
| 514 | |||
| 515 | switch (this.op) { | ||
| 516 | case RrdVdef.VDEF_PERCENT: | ||
| 517 | var array = []; | ||
| 518 | var field; | ||
| 519 | |||
| 520 | for (step = 0; step < steps; step++) { | ||
| 521 | array[step] = data[step * src.ds_cnt]; | ||
| 522 | } | ||
| 523 | array.sort(this.vdef_percent_compar); | ||
| 524 | field = Math.round((this.param * (steps - 1)) / 100.0); | ||
| 525 | this.val = array[field]; | ||
| 526 | this.when = 0; /* no time component */ | ||
| 527 | break; | ||
| 528 | case RrdVdef.VDEF_PERCENTNAN: | ||
| 529 | var array = []; | ||
| 530 | var field; | ||
| 531 | |||
| 532 | field=0; | ||
| 533 | for (step = 0; step < steps; step++) { | ||
| 534 | if (!isNaN(data[step * src.ds_cnt])) { | ||
| 535 | array[field] = data[step * src.ds_cnt]; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | array.sort(vdef_percent_compar); | ||
| 539 | field = Math.round(this.param * (field - 1) / 100.0); | ||
| 540 | his.val = array[field]; | ||
| 541 | this.when = 0; /* no time component */ | ||
| 542 | break; | ||
| 543 | case RrdVdef.VDEF_MAXIMUM: | ||
| 544 | step = 0; | ||
| 545 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
| 546 | if (step === steps) { | ||
| 547 | this.val = Number.NaN; | ||
| 548 | this.when = 0; | ||
| 549 | } else { | ||
| 550 | this.val = data[step * src.ds_cnt]; | ||
| 551 | this.when = src.start + (step + 1) * src.step; | ||
| 552 | } | ||
| 553 | while (step != steps) { | ||
| 554 | if (isFinite(data[step * src.ds_cnt])) { | ||
| 555 | if (data[step * src.ds_cnt] > this.val) { | ||
| 556 | this.val = data[step * src.ds_cnt]; | ||
| 557 | this.when = src.start + (step + 1) * src.step; | ||
| 558 | } | ||
| 559 | } | ||
| 560 | step++; | ||
| 561 | } | ||
| 562 | break; | ||
| 563 | case RrdVdef.VDEF_TOTAL: | ||
| 564 | case RrdVdef.VDEF_STDEV: | ||
| 565 | case RrdVdef.VDEF_AVERAGE: | ||
| 566 | var cnt = 0; | ||
| 567 | var sum = 0.0; | ||
| 568 | var average = 0.0; | ||
| 569 | |||
| 570 | for (step = 0; step < steps; step++) { | ||
| 571 | if (isFinite(data[step * src.ds_cnt])) { | ||
| 572 | sum += data[step * src.ds_cnt]; | ||
| 573 | cnt++; | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | if (cnt) { | ||
| 578 | if (this.op === RrdVdef.VDEF_TOTAL) { | ||
| 579 | this.val = sum * src.step; | ||
| 580 | this.when = 0; /* no time component */ | ||
| 581 | } else if (this.op === RrdVdef.VDEF_AVERAGE) { | ||
| 582 | this.val = sum / cnt; | ||
| 583 | this.when = 0; /* no time component */ | ||
| 584 | } else { | ||
| 585 | average = sum / cnt; | ||
| 586 | sum = 0.0; | ||
| 587 | for (step = 0; step < steps; step++) { | ||
| 588 | if (isFinite(data[step * src.ds_cnt])) { | ||
| 589 | sum += Math.pow((data[step * src.ds_cnt] - average), 2.0); | ||
| 590 | } | ||
| 591 | } | ||
| 592 | this.val = Math.pow(sum / cnt, 0.5); | ||
| 593 | this.when = 0; /* no time component */ | ||
| 594 | } | ||
| 595 | } else { | ||
| 596 | this.val = Number.NaN; | ||
| 597 | this.when = 0; | ||
| 598 | } | ||
| 599 | break; | ||
| 600 | case RrdVdef.VDEF_MINIMUM: | ||
| 601 | step = 0; | ||
| 602 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
| 603 | if (step === steps) { | ||
| 604 | this.val = Number.NaN; | ||
| 605 | this.when = 0; | ||
| 606 | } else { | ||
| 607 | this.val = data[step * src.ds_cnt]; | ||
| 608 | this.when = src.start + (step + 1) * src.step; | ||
| 609 | } | ||
| 610 | while (step != steps) { | ||
| 611 | if (isFinite(data[step * src.ds_cnt])) { | ||
| 612 | if (data[step * src.ds_cnt] < this.val) { | ||
| 613 | this.val = data[step * src.ds_cnt]; | ||
| 614 | this.when = src.start + (step + 1) * src.step; | ||
| 615 | } | ||
| 616 | } | ||
| 617 | step++; | ||
| 618 | } | ||
| 619 | break; | ||
| 620 | case RrdVdef.VDEF_FIRST: | ||
| 621 | step = 0; | ||
| 622 | while (step != steps && isNaN(data[step * src.ds_cnt])) step++; | ||
| 623 | if (step === steps) { /* all entries were NaN */ | ||
| 624 | this.val = Number.NaN; | ||
| 625 | this.when = 0; | ||
| 626 | } else { | ||
| 627 | this.val = data[step * src.ds_cnt]; | ||
| 628 | this.when = src.start + step * src.step; | ||
| 629 | } | ||
| 630 | break; | ||
| 631 | case RrdVdef.VDEF_LAST: | ||
| 632 | step = steps - 1; | ||
| 633 | while (step >= 0 && isNaN(data[step * src.ds_cnt])) step--; | ||
| 634 | if (step < 0) { /* all entries were NaN */ | ||
| 635 | this.val = Number.NaN; | ||
| 636 | this.when = 0; | ||
| 637 | } else { | ||
| 638 | this.val = data[step * src.ds_cnt]; | ||
| 639 | this.when = src.start + (step + 1) * src.step; | ||
| 640 | } | ||
| 641 | break; | ||
| 642 | case RrdVdef.VDEF_LSLSLOPE: | ||
| 643 | case RrdVdef.VDEF_LSLINT: | ||
| 644 | case RrdVdef.VDEF_LSLCORREL: | ||
| 645 | var cnt = 0; | ||
| 646 | var SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl; | ||
| 647 | |||
| 648 | SUMx = 0; | ||
| 649 | SUMy = 0; | ||
| 650 | SUMxy = 0; | ||
| 651 | SUMxx = 0; | ||
| 652 | SUMyy = 0; | ||
| 653 | |||
| 654 | for (step = 0; step < steps; step++) { | ||
| 655 | if (isFinite(data[step * src.ds_cnt])) { | ||
| 656 | cnt++; | ||
| 657 | SUMx += step; | ||
| 658 | SUMxx += step * step; | ||
| 659 | SUMxy += step * data[step * src.ds_cnt]; | ||
| 660 | SUMy += data[step * src.ds_cnt]; | ||
| 661 | SUMyy += data[step * src.ds_cnt] * data[step * src.ds_cnt]; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | slope = (SUMx * SUMy - cnt * SUMxy) / (SUMx * SUMx - cnt * SUMxx); | ||
| 666 | y_intercept = (SUMy - slope * SUMx) / cnt; | ||
| 667 | correl = (SUMxy - (SUMx * SUMy) / cnt) / Math.sqrt((SUMxx - (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt)); | ||
| 668 | |||
| 669 | if (cnt) { | ||
| 670 | if (this.op === RrdVdef.VDEF_LSLSLOPE) { | ||
| 671 | this.val = slope; | ||
| 672 | this.when = 0; | ||
| 673 | } else if (this.op === RrdVdef.VDEF_LSLINT) { | ||
| 674 | this.val = y_intercept; | ||
| 675 | this.when = 0; | ||
| 676 | } else if (this.op === RrdVdef.VDEF_LSLCORREL) { | ||
| 677 | this.val = correl; | ||
| 678 | this.when = 0; | ||
| 679 | } | ||
| 680 | } else { | ||
| 681 | this.val = Number.NaN; | ||
| 682 | this.when = 0; | ||
| 683 | } | ||
| 684 | break; | ||
| 685 | } | ||
| 686 | return 0; | ||
| 687 | }; | ||
| 688 | |||
| 689 | /** | ||
| 690 | * RrdGraphError | ||
| 691 | * @constructor | ||
| 692 | */ | ||
| 693 | var RrdGraphError = function (message) | ||
| 694 | { | ||
| 695 | this.prototype = Error.prototype; | ||
| 696 | this.name = "RrdGraphError"; | ||
| 697 | this.message = (message) ? message : "Error"; | ||
| 698 | }; | ||
| 699 | |||
| 700 | /** | ||
| 701 | * RrdGraph | ||
| 702 | * @constructor | ||
| 703 | */ | ||
| 704 | var RrdGraph = function (gfx, data) | ||
| 705 | { | ||
| 706 | this.gfx = gfx; /* graphics object */ | ||
| 707 | this.data = data; /* fetch data object */ | ||
| 708 | |||
| 709 | this.minval = Number.NaN; /* extreme values in the data */ | ||
| 710 | this.maxval = Number.NaN; | ||
| 711 | /* status information */ | ||
| 712 | //with_markup: 0, | ||
| 713 | this.xorigin = 0; /* where is (0,0) of the graph */ | ||
| 714 | this.yorigin = 0; | ||
| 715 | this.xOriginTitle = 0; /* where is the origin of the title */ | ||
| 716 | this.yOriginTitle = 0; | ||
| 717 | this.xOriginLegendY = 0; /* where is the origin of the y legend */ | ||
| 718 | this.yOriginLegendY = 0; | ||
| 719 | this.xOriginLegendY2 = 0; /* where is the origin of the second y legend */ | ||
| 720 | this.yOriginLegendY2 = 0; | ||
| 721 | this.xOriginLegend = 0; /* where is the origin of the legend */ | ||
| 722 | this.yOriginLegend = 0; | ||
| 723 | this.ximg = 0; /* total size of the image */ | ||
| 724 | this.yimg = 0; | ||
| 725 | this.legendwidth = 0; /* the calculated height and width of the legend */ | ||
| 726 | this.legendheight = 0; | ||
| 727 | this.magfact = 1; /* numerical magnitude */ | ||
| 728 | this.symbol = null; /* magnitude symbol for y-axis */ | ||
| 729 | this.viewfactor = 1.0; /* how should the numbers on the y-axis be scaled for viewing ? */ | ||
| 730 | |||
| 731 | this.base = 1000; /* 1000 or 1024 depending on what we graph */ | ||
| 732 | |||
| 733 | this.start = 0; /* what time does the graph cover */ | ||
| 734 | this.end = 0; | ||
| 735 | |||
| 736 | this.xlab_form = null; /* format for the label on the xaxis */ | ||
| 737 | /* public */ | ||
| 738 | this.xsize = 400; /* graph area size in pixels */ | ||
| 739 | this.ysize = 100; | ||
| 740 | this.zoom = 1; | ||
| 741 | this.grid_dash_on = 1; | ||
| 742 | this.grid_dash_off = 1; | ||
| 743 | this.second_axis_scale = 0; /* relative to the first axis (0 to disable) */ | ||
| 744 | this.second_axis_shift = 0; /* how much is it shifted vs the first axis */ | ||
| 745 | this.second_axis_legend = null; /* label to put on the seond axis */ | ||
| 746 | this.second_axis_format = null; /* format for the numbers on the scond axis */ | ||
| 747 | this.draw_x_grid = true; /* no x-grid at all */ | ||
| 748 | this.draw_y_grid = true; /* no y-grid at all */ | ||
| 749 | this.ygridstep = Number.NaN; /* user defined step for y grid */ | ||
| 750 | this.ylabfact = 0; /* every how many y grid shall a label be written ? */ | ||
| 751 | this.draw_3d_border = 2; /* size of border in pixels, 0 for off */ | ||
| 752 | this.dynamic_labels = false;/* pick the label shape according to the line drawn */ | ||
| 753 | this.ylegend = null; /* legend along the yaxis */ | ||
| 754 | this.title = ''; /* title for graph */ | ||
| 755 | this.watermark = null; /* watermark for graph */ | ||
| 756 | this.tabwidth = 40; /* tabwdith */ | ||
| 757 | this.step = 0; /* any preference for the default step ? */ | ||
| 758 | this.setminval = Number.NaN; /* extreme values in the data */ | ||
| 759 | this.setmaxval = Number.NaN; | ||
| 760 | this.rigid = false; /* do not expand range even with values outside */ | ||
| 761 | this.gridfit = true; /* adjust y-axis range etc so all grindlines falls in integer pixel values */ | ||
| 762 | this.lazy = 0; /* only update the image if there is reasonable probablility that the existing one is out of date */ | ||
| 763 | this.legendposition = RrdGraph.LEGEND_POS_SOUTH; /* the position of the legend: north, west, south or east */ | ||
| 764 | this.legenddirection = RrdGraph.LEGEND_DIR_TOP_DOWN; /* The direction of the legend topdown or bottomup */ | ||
| 765 | this.logarithmic = false; /* scale the yaxis logarithmic */ | ||
| 766 | this.force_scale_min = 0; /* Force a scale--min */ | ||
| 767 | this.force_scale_max = 0; /* Force a scale--max */ | ||
| 768 | this.unitsexponent = 9999; /* 10*exponent for units on y-asis */ | ||
| 769 | this.unitslength = 6; /* width of the yaxis labels */ | ||
| 770 | this.forceleftspace = false; /* do not kill the space to the left of the y-axis if there is no grid */ | ||
| 771 | this.slopemode = false; /* connect the dots of the curve directly, not using a stair */ | ||
| 772 | this.alt_ygrid = false; /* use alternative y grid algorithm */ | ||
| 773 | this.alt_autoscale = false; /* use alternative algorithm to find lower and upper bounds */ | ||
| 774 | this.alt_autoscale_min = false; /* use alternative algorithm to find lower bounds */ | ||
| 775 | this.alt_autoscale_max = false; /* use alternative algorithm to find upper bounds */ | ||
| 776 | this.no_legend = false; /* use no legend */ | ||
| 777 | this.no_minor = false; /* Turn off minor gridlines */ | ||
| 778 | this.only_graph = false; /* use only graph */ | ||
| 779 | this.force_rules_legend = false; /* force printing of HRULE and VRULE legend */ | ||
| 780 | this.force_units = false; /* mask for all FORCE_UNITS_* flags */ | ||
| 781 | this.force_units_si = false; /* force use of SI units in Y axis (no effect in linear graph, SI instead of E in log graph) */ | ||
| 782 | this.full_size_mode = false; /* -width and -height indicate the total size of the image */ | ||
| 783 | this.no_rrdtool_tag = false; /* disable the rrdtool tag */ | ||
| 784 | |||
| 785 | this.xlab_user = null; | ||
| 786 | this.ygrid_scale = null; | ||
| 787 | |||
| 788 | this.gdes = []; | ||
| 789 | |||
| 790 | this.ytr_pixie = 0; | ||
| 791 | this.xtr_pixie = 0; | ||
| 792 | |||
| 793 | this.AlmostEqualBuffer = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT*2); | ||
| 794 | this.AlmostEqualInt = new Int32Array(this.AlmostEqualBuffer); | ||
| 795 | this.AlmostEqualFloat = new Float32Array(this.AlmostEqualBuffer); | ||
| 796 | |||
| 797 | this.DEFAULT_FONT = 'DejaVu Sans Mono'; //DejaVu Sans Mono ,Bitstream Vera Sans Mono,monospace,Courier', // pt -> pt=px*72/96 | ||
| 798 | this.MGRIDWIDTH = 0.6; | ||
| 799 | this.GRIDWIDTH = 0.4; | ||
| 800 | this.YLEGEND_ANGLE = 90.0; | ||
| 801 | |||
| 802 | this.TEXT = { | ||
| 803 | DEFAULT: { size: 11, font: this.DEFAULT_FONT }, | ||
| 804 | TITLE: { size: 12, font: this.DEFAULT_FONT }, | ||
| 805 | AXIS: { size: 10, font: this.DEFAULT_FONT }, | ||
| 806 | UNIT: { size: 11, font: this.DEFAULT_FONT }, | ||
| 807 | LEGEND: { size: 11, font: this.DEFAULT_FONT }, | ||
| 808 | WATERMARK: { size: 8, font: this.DEFAULT_FONT } | ||
| 809 | }; | ||
| 810 | |||
| 811 | this.GRC = { | ||
| 812 | CANVAS: 'rgba(255, 255, 255, 1.0)', | ||
| 813 | BACK: 'rgba(242,242, 242, 1.0)', | ||
| 814 | SHADEA: 'rgba(207, 207, 207, 1.0)', | ||
| 815 | SHADEB: 'rgba(158, 158, 158, 1.0)', | ||
| 816 | GRID: 'rgba(143, 143, 143, 0.75)', | ||
| 817 | MGRID: 'rgba(222, 79, 79, 0.60)', | ||
| 818 | FONT: 'rgba(0, 0, 0, 1.0)', | ||
| 819 | ARROW: 'rgba(127, 31, 31, 1.0)', | ||
| 820 | AXIS: 'rgba(31, 31, 31, 1.0)', | ||
| 821 | FRAME: 'rgba(0, 0, 0, 1.0)' | ||
| 822 | }; | ||
| 823 | |||
| 824 | this.xlab = [ | ||
| 825 | {minsec: 0, length: 0, gridtm: RrdGraph.TMT_SECOND, gridst: 30, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 5, labtm: RrdGraph.TMT_MINUTE, labst: 5, precis: 0, stst: '%H:%M' } , | ||
| 826 | {minsec: 2, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 1, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 5, labtm: RrdGraph.TMT_MINUTE, labst: 5, precis: 0, stst: '%H:%M' } , | ||
| 827 | {minsec: 5, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 2, mgridtm: RrdGraph.TMT_MINUTE, mgridst: 10, labtm: RrdGraph.TMT_MINUTE, labst: 10, precis: 0, stst: '%H:%M' } , | ||
| 828 | {minsec: 10, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 5,mgridtm: RrdGraph.TMT_MINUTE, mgridst: 20, labtm: RrdGraph.TMT_MINUTE, labst: 20, precis: 0, stst: '%H:%M' } , | ||
| 829 | {minsec: 30, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 10, mgridtm: RrdGraph.TMT_HOUR, mgridst: 1, labtm: RrdGraph.TMT_HOUR, labst: 1, precis: 0, stst: '%H:%M' } , | ||
| 830 | {minsec: 60, length: 0, gridtm: RrdGraph.TMT_MINUTE, gridst: 30, mgridtm: RrdGraph.TMT_HOUR, mgridst: 2, labtm: RrdGraph.TMT_HOUR, labst: 2, precis: 0, stst: '%H:%M' } , | ||
| 831 | {minsec: 60, length: 24 * 3600, gridtm: RrdGraph.TMT_MINUTE, gridst: 30, mgridtm: RrdGraph.TMT_HOUR, mgridst: 2, labtm: RrdGraph.TMT_HOUR, labst: 6, precis: 0, stst: '%a %H:%M' } , | ||
| 832 | {minsec: 180, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 1, mgridtm: RrdGraph.TMT_HOUR, mgridst: 6, labtm: RrdGraph.TMT_HOUR, labst: 6, precis: 0, stst: '%H:%M' } , | ||
| 833 | {minsec: 180, length: 24 * 3600, gridtm: RrdGraph.TMT_HOUR, gridst: 1, mgridtm: RrdGraph.TMT_HOUR, mgridst: 6, labtm: RrdGraph.TMT_HOUR, labst: 12, precis: 0, stst: '%a %H:%M' } , | ||
| 834 | {minsec: 600, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 6, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 1, precis: 24 * 3600, stst: '%a' } , | ||
| 835 | {minsec: 1200, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 6, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 1, precis: 24 * 3600, stst: '%d' } , | ||
| 836 | {minsec: 1800, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 12, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 2, precis: 24 * 3600, stst: '%a %d' }, | ||
| 837 | {minsec: 2400, length: 0, gridtm: RrdGraph.TMT_HOUR, gridst: 12, mgridtm: RrdGraph.TMT_DAY, mgridst: 1, labtm: RrdGraph.TMT_DAY, labst: 2, precis: 24 * 3600,stst: '%a' }, | ||
| 838 | {minsec: 3600, length: 0, gridtm: RrdGraph.TMT_DAY, gridst: 1, mgridtm: RrdGraph.TMT_WEEK, mgridst: 1, labtm: RrdGraph.TMT_WEEK, labst: 1, precis: 7 * 24 * 3600, stst: 'Week %V' }, | ||
| 839 | {minsec: 3 * 3600, length: 0, gridtm: RrdGraph.TMT_WEEK, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 1, labtm: RrdGraph.TMT_WEEK, labst: 2, precis: 7 * 24 * 3600, stst: 'Week %V' }, | ||
| 840 | {minsec: 6 * 3600, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 1, labtm: RrdGraph.TMT_MONTH, labst: 1, precis: 30 * 24 * 3600, stst: '%b' }, | ||
| 841 | {minsec: 48 * 3600,length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 1, mgridtm: RrdGraph.TMT_MONTH, mgridst: 3, labtm: RrdGraph.TMT_MONTH, labst: 3, precis: 30 * 24 * 3600, stst: '%b' }, | ||
| 842 | {minsec: 315360, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 3, mgridtm: RrdGraph.TMT_YEAR, mgridst: 1, labtm: RrdGraph.TMT_YEAR, labst: 1, precis: 365 * 24 * 3600, stst: '%Y' }, | ||
| 843 | {minsec: 10 * 24 * 3600 , length: 0, gridtm: RrdGraph.TMT_YEAR, gridst: 1, mgridtm: RrdGraph.TMT_YEAR, mgridst: 1, labtm: RrdGraph.TMT_YEAR, labst: 1, precis: 365 * 24 * 3600, stst: '%y' }, | ||
| 844 | {minsec: -1, length: 0, gridtm: RrdGraph.TMT_MONTH, gridst: 0, mgridtm: RrdGraph.TMT_MONTH, mgridst: 0, labtm: RrdGraph.TMT_MONTH, labst: 0, precis: 0, stst: null } | ||
| 845 | ]; | ||
| 846 | |||
| 847 | this.ylab = [ | ||
| 848 | {grid: 0.1, lfac: [1, 2, 5, 10] } , | ||
| 849 | {grid: 0.2, lfac: [1, 5, 10, 20] } , | ||
| 850 | {grid: 0.5, lfac: [1, 2, 4, 10] } , | ||
| 851 | {grid: 1.0, lfac: [1, 2, 5, 10] } , | ||
| 852 | {grid: 2.0, lfac: [1, 5, 10, 20] } , | ||
| 853 | {grid: 5.0, lfac: [1, 2, 4, 10] } , | ||
| 854 | {grid: 10.0, lfac: [1, 2, 5, 10] } , | ||
| 855 | {grid: 20.0, lfac: [1, 5, 10, 20] } , | ||
| 856 | {grid: 50.0, lfac: [1, 2, 4, 10] } , | ||
| 857 | {grid: 100.0, lfac: [1, 2, 5, 10] } , | ||
| 858 | {grid: 200.0, lfac: [1, 5, 10, 20] } , | ||
| 859 | {grid: 500.0, lfac: [1, 2, 4, 10] }, | ||
| 860 | {grid: 0.0, lfac: [0, 0, 0, 0] } | ||
| 861 | ]; | ||
| 862 | |||
| 863 | this.si_symbol = [ | ||
| 864 | 'y', /* 10e-24 Yocto */ | ||
| 865 | 'z', /* 10e-21 Zepto */ | ||
| 866 | 'a', /* 10e-18 Atto */ | ||
| 867 | 'f', /* 10e-15 Femto */ | ||
| 868 | 'p', /* 10e-12 Pico */ | ||
| 869 | 'n', /* 10e-9 Nano */ | ||
| 870 | 'u', /* 10e-6 Micro */ | ||
| 871 | 'm', /* 10e-3 Milli */ | ||
| 872 | ' ', /* Base */ | ||
| 873 | 'k', /* 10e3 Kilo */ | ||
| 874 | 'M', /* 10e6 Mega */ | ||
| 875 | 'G', /* 10e9 Giga */ | ||
| 876 | 'T', /* 10e12 Tera */ | ||
| 877 | 'P', /* 10e15 Peta */ | ||
| 878 | 'E', /* 10e18 Exa */ | ||
| 879 | 'Z', /* 10e21 Zeta */ | ||
| 880 | 'Y' /* 10e24 Yotta */ | ||
| 881 | ]; | ||
| 882 | this.si_symbcenter = 8; | ||
| 883 | |||
| 884 | this.start_t = new RrdTime("end-24h"); | ||
| 885 | this.end_t = new RrdTime("now"); | ||
| 886 | }; | ||
| 887 | |||
| 888 | RrdGraph.TMT_SECOND = 0; | ||
| 889 | RrdGraph.TMT_MINUTE = 1; | ||
| 890 | RrdGraph.TMT_HOUR = 2; | ||
| 891 | RrdGraph.TMT_DAY = 3; | ||
| 892 | RrdGraph.TMT_WEEK = 4; | ||
| 893 | RrdGraph.TMT_MONTH = 5; | ||
| 894 | RrdGraph.TMT_YEAR = 6; | ||
| 895 | |||
| 896 | RrdGraph.GFX_H_LEFT = 1; | ||
| 897 | RrdGraph.GFX_H_RIGHT = 2; | ||
| 898 | RrdGraph.GFX_H_CENTER = 3; | ||
| 899 | |||
| 900 | RrdGraph.GFX_V_TOP = 1; | ||
| 901 | RrdGraph.GFX_V_BOTTOM = 2; | ||
| 902 | RrdGraph.GFX_V_CENTER = 3; | ||
| 903 | |||
| 904 | RrdGraph.LEGEND_POS_NORTH = 0; | ||
| 905 | RrdGraph.LEGEND_POS_WEST = 1; | ||
| 906 | RrdGraph.LEGEND_POS_SOUTH = 2; | ||
| 907 | RrdGraph.LEGEND_POS_EAST = 3; | ||
| 908 | |||
| 909 | RrdGraph.LEGEND_DIR_TOP_DOWN = 0; | ||
| 910 | RrdGraph.LEGEND_DIR_BOTTOM_UP = 1; | ||
| 911 | |||
| 912 | RrdGraph.prototype.set_default_font = function (name) | ||
| 913 | { | ||
| 914 | for (var font in this.TEXT) | ||
| 915 | this.TEXT[font].font = name; | ||
| 916 | }; | ||
| 917 | |||
| 918 | RrdGraph.prototype.parse_color = function(str) | ||
| 919 | { | ||
| 920 | var bits; | ||
| 921 | if ((bits = /^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/.exec(str))) { | ||
| 922 | return [parseInt(bits[1]+bits[1], 16), parseInt(bits[2]+bits[2], 16), parseInt(bits[3]+bits[3], 16), 1.0]; | ||
| 923 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 924 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), 1.0]; | ||
| 925 | } else if ((bits = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(str))) { | ||
| 926 | return [parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16), parseInt(bits[4], 16)/255]; | ||
| 927 | } else if ((bits = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/.exec(str))) { | ||
| 928 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), 1.0]; | ||
| 929 | } else if ((bits = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([0-9.]+)\)$/.exec(str))) { | ||
| 930 | return [parseInt(bits[1], 10), parseInt(bits[2], 10), parseInt(bits[3], 10), parseFloat(bits[4], 10)]; | ||
| 931 | } else { | ||
| 932 | throw new RrdGraphError("Unknow color format '"+str+"'"); | ||
| 933 | } | ||
| 934 | }; | ||
| 935 | |||
| 936 | RrdGraph.prototype.color2rgba = function (color) | ||
| 937 | { | ||
| 938 | return 'rgba('+color[0]+','+color[1]+','+color[2]+','+color[3]+')'; | ||
| 939 | }; | ||
| 940 | |||
| 941 | RrdGraph.prototype.xtr = function (mytime) | ||
| 942 | { | ||
| 943 | if (mytime === 0) { | ||
| 944 | this.xtr_pixie = this.xsize / (this.end - this.start); | ||
| 945 | return this.xorigin; | ||
| 946 | } | ||
| 947 | return this.xorigin + this.xtr_pixie * (mytime - this.start); | ||
| 948 | }; | ||
| 949 | |||
| 950 | RrdGraph.prototype.ytr = function (value) | ||
| 951 | { | ||
| 952 | var yval; | ||
| 953 | |||
| 954 | if (isNaN(value)) { | ||
| 955 | if (!this.logarithmic) | ||
| 956 | this.ytr_pixie = this.ysize / (this.maxval - this.minval); | ||
| 957 | else | ||
| 958 | this.ytr_pixie = this.ysize / ((Math.log(this.maxval)/Math.LN10) - (Math.log(this.minval)/Math.LN10)); | ||
| 959 | yval = this.yorigin; | ||
| 960 | } else if (!this.logarithmic) { | ||
| 961 | yval = this.yorigin - this.ytr_pixie * (value - this.minval); | ||
| 962 | } else { | ||
| 963 | if (value < this.minval) { | ||
| 964 | yval = this.yorigin; | ||
| 965 | } else { | ||
| 966 | yval = this.yorigin - this.ytr_pixie * ((Math.log(value)/Math.LN10) - (Math.log(this.minval)/Math.LN10)); | ||
| 967 | } | ||
| 968 | } | ||
| 969 | return yval; | ||
| 970 | }; | ||
| 971 | |||
| 972 | RrdGraph.prototype.AlmostEqual2sComplement = function(A, B, maxUlps) | ||
| 973 | { | ||
| 974 | this.AlmostEqualFloat[0] = A; | ||
| 975 | this.AlmostEqualFloat[1] = B; | ||
| 976 | |||
| 977 | var aInt = this.AlmostEqualInt[0]; | ||
| 978 | if (aInt < 0) aInt = 0x80000000 - aInt; | ||
| 979 | |||
| 980 | var bInt = this.AlmostEqualInt[1]; | ||
| 981 | if (bInt < 0) bInt = 0x80000000 - bInt; | ||
| 982 | |||
| 983 | var intDiff = Math.abs(aInt - bInt); | ||
| 984 | |||
| 985 | if (intDiff <= maxUlps) | ||
| 986 | return true; | ||
| 987 | |||
| 988 | return false; | ||
| 989 | }; | ||
| 990 | |||
| 991 | RrdGraph.prototype.tmt2str = function (val) | ||
| 992 | { | ||
| 993 | switch (val) { | ||
| 994 | case RrdGraph.TMT_SECOND: return 'sec'; | ||
| 995 | case RrdGraph.TMT_MINUTE: return 'min'; | ||
| 996 | case RrdGraph.TMT_HOUR: return 'hour'; | ||
| 997 | case RrdGraph.TMT_DAY: return 'day'; | ||
| 998 | case RrdGraph.TMT_WEEK: return 'week'; | ||
| 999 | case RrdGraph.TMT_MONTH: return 'mon'; | ||
| 1000 | case RrdGraph.TMT_YEAR: return 'year'; | ||
| 1001 | } | ||
| 1002 | return val; | ||
| 1003 | }; | ||
| 1004 | |||
| 1005 | RrdGraph.prototype.find_first_time = function(start, baseint, basestep) | ||
| 1006 | { | ||
| 1007 | var date = new Date(start*1000); | ||
| 1008 | |||
| 1009 | switch (baseint) { | ||
| 1010 | case RrdGraph.TMT_SECOND: | ||
| 1011 | var sec = date.getSeconds(); | ||
| 1012 | sec -= sec % basestep; | ||
| 1013 | date.setSeconds(sec); | ||
| 1014 | break; | ||
| 1015 | case RrdGraph.TMT_MINUTE: | ||
| 1016 | date.setSeconds(0); | ||
| 1017 | var min = date.getMinutes(); | ||
| 1018 | min -= min % basestep; | ||
| 1019 | date.setMinutes(min); | ||
| 1020 | break; | ||
| 1021 | case RrdGraph.TMT_HOUR: | ||
| 1022 | date.setSeconds(0); | ||
| 1023 | date.setMinutes(0); | ||
| 1024 | var hour = date.getHours(); | ||
| 1025 | hour -= hour % basestep; | ||
| 1026 | date.setHours(hour); | ||
| 1027 | break; | ||
| 1028 | case RrdGraph.TMT_DAY: | ||
| 1029 | date.setSeconds(0); | ||
| 1030 | date.setMinutes(0); | ||
| 1031 | date.setHours(0); | ||
| 1032 | break; | ||
| 1033 | case RrdGraph.TMT_WEEK: | ||
| 1034 | date.setSeconds(0); | ||
| 1035 | date.setMinutes(0); | ||
| 1036 | date.setHours(0); | ||
| 1037 | var mday = date.getDate(); | ||
| 1038 | var wday = date.getDay(); | ||
| 1039 | mday -= wday - 1; // FIXME find_first_weekday | ||
| 1040 | if (wday === 0) mday -= 7;// FIXME find_first_weekday | ||
| 1041 | date.setDate(mday); | ||
| 1042 | break; | ||
| 1043 | case RrdGraph.TMT_MONTH: | ||
| 1044 | date.setSeconds(0); | ||
| 1045 | date.setMinutes(0); | ||
| 1046 | date.setHours(0); | ||
| 1047 | date.setDate(1); | ||
| 1048 | var mon = date.getMonth(); | ||
| 1049 | mon -= mon % basestep; | ||
| 1050 | date.setMonth(mon); | ||
| 1051 | break; | ||
| 1052 | case RrdGraph.TMT_YEAR: | ||
| 1053 | date.setSeconds(0); | ||
| 1054 | date.setMinutes(0); | ||
| 1055 | date.setHours(0); | ||
| 1056 | date.setDate(1); | ||
| 1057 | date.setMonth(0); | ||
| 1058 | var year = date.getFullYear()-1900; | ||
| 1059 | year -= (year + 1900) %basestep; | ||
| 1060 | date.setFullYear(year+1900); | ||
| 1061 | } | ||
| 1062 | return Math.round(date.getTime()/1000.0); | ||
| 1063 | }; | ||
| 1064 | |||
| 1065 | RrdGraph.prototype.find_next_time = function(current, baseint, basestep) | ||
| 1066 | { | ||
| 1067 | var date = new Date(current*1000); | ||
| 1068 | var limit = 2; | ||
| 1069 | var madetime; | ||
| 1070 | |||
| 1071 | switch (baseint) { | ||
| 1072 | case RrdGraph.TMT_SECOND: limit = 7200; break; | ||
| 1073 | case RrdGraph.TMT_MINUTE: limit = 120; break; | ||
| 1074 | case RrdGraph.TMT_HOUR: limit = 2; break; | ||
| 1075 | default: limit = 2; break; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | do { | ||
| 1079 | switch (baseint) { | ||
| 1080 | case RrdGraph.TMT_SECOND: | ||
| 1081 | date.setSeconds(date.getSeconds()+basestep); | ||
| 1082 | break; | ||
| 1083 | case RrdGraph.TMT_MINUTE: | ||
| 1084 | date.setMinutes(date.getMinutes()+basestep); | ||
| 1085 | break; | ||
| 1086 | case RrdGraph.TMT_HOUR: | ||
| 1087 | date.setHours(date.getHours()+basestep); | ||
| 1088 | break; | ||
| 1089 | case RrdGraph.TMT_DAY: | ||
| 1090 | date.setDate(date.getDate()+basestep); | ||
| 1091 | break; | ||
| 1092 | case RrdGraph.TMT_WEEK: | ||
| 1093 | date.setDate(date.getDate()+7*basestep); | ||
| 1094 | break; | ||
| 1095 | case RrdGraph.TMT_MONTH: | ||
| 1096 | date.setMonth(date.getMonth()+basestep); | ||
| 1097 | break; | ||
| 1098 | case RrdGraph.TMT_YEAR: | ||
| 1099 | date.setFullYear(date.getFullYear()+basestep); | ||
| 1100 | break; | ||
| 1101 | } | ||
| 1102 | madetime = Math.round(date.getTime()/1000.0); | ||
| 1103 | } while (madetime === -1 && limit-- >= 0); /* this is necessary to skip impossible times like the daylight saving time skips */ // FIXME ?? | ||
| 1104 | return madetime; | ||
| 1105 | }; | ||
| 1106 | |||
| 1107 | RrdGraph.prototype.print_calc = function() | ||
| 1108 | { | ||
| 1109 | var validsteps; | ||
| 1110 | var printval; | ||
| 1111 | var tmvdef; | ||
| 1112 | var graphelement = 0; | ||
| 1113 | var max_ii; | ||
| 1114 | var magfact = -1; | ||
| 1115 | var si_symb = ""; | ||
| 1116 | var percent_s; | ||
| 1117 | var prline_cnt = 0; | ||
| 1118 | |||
| 1119 | var now = Math.round((new Date()).getTime() / 1000); | ||
| 1120 | |||
| 1121 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 1122 | var vidx = this.gdes[i].vidx; | ||
| 1123 | switch (this.gdes[i].gf) { | ||
| 1124 | case RrdGraphDesc.GF_PRINT: | ||
| 1125 | case RrdGraphDesc.GF_GPRINT: | ||
| 1126 | if (this.gdes[vidx].gf === RrdGraphDesc.GF_VDEF) { /* simply use vals */ | ||
| 1127 | printval = this.gdes[vidx].vf.val; | ||
| 1128 | tmvdef = this.gdes[vidx].vf.when; | ||
| 1129 | // localtime_r(&this.gdes[vidx].vf.when, &tmvdef); // FIXME ? | ||
| 1130 | } else { /* need to calculate max,min,avg etcetera */ | ||
| 1131 | max_ii = ((this.gdes[vidx].end - this.gdes[vidx].start) / this.gdes[vidx].step * this.gdes[vidx].ds_cnt); | ||
| 1132 | printval = Number.NaN; | ||
| 1133 | validsteps = 0; | ||
| 1134 | |||
| 1135 | for (var ii = this.gdes[vidx].ds; ii < max_ii; ii += this.gdes[vidx].ds_cnt) { | ||
| 1136 | if (!isFinite(this.gdes[vidx].data[ii])) continue; | ||
| 1137 | if (isNaN(printval)) { | ||
| 1138 | printval = this.gdes[vidx].data[ii]; | ||
| 1139 | validsteps++; | ||
| 1140 | continue; | ||
| 1141 | } | ||
| 1142 | switch (this.gdes[i].cf) { | ||
| 1143 | case RrdGraphDesc.CF_HWPREDICT: | ||
| 1144 | case RrdGraphDesc.CF_MHWPREDICT: | ||
| 1145 | case RrdGraphDesc.CF_DEVPREDICT: | ||
| 1146 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
| 1147 | case RrdGraphDesc.CF_SEASONAL: | ||
| 1148 | case RrdGraphDesc.CF_AVERAGE: | ||
| 1149 | validsteps++; | ||
| 1150 | printval += this.gdes[vidx].data[ii]; | ||
| 1151 | break; | ||
| 1152 | case RrdGraphDesc.CF_MINIMUM: | ||
| 1153 | printval = Math.min(printval, this.gdes[vidx].data[ii]); | ||
| 1154 | break; | ||
| 1155 | case RrdGraphDesc.CF_FAILURES: | ||
| 1156 | case RrdGraphDesc.CF_MAXIMUM: | ||
| 1157 | printval = Math.max(printval, this.gdes[vidx].data[ii]); | ||
| 1158 | break; | ||
| 1159 | case RrdGraphDesc.CF_LAST: | ||
| 1160 | printval = this.gdes[vidx].data[ii]; | ||
| 1161 | } | ||
| 1162 | } | ||
| 1163 | if (this.gdes[i].cf === RrdGraphDesc.CF_AVERAGE || this.gdes[i].cf > RrdGraphDesc.CF_LAST) { | ||
| 1164 | if (validsteps > 1) printval = (printval / validsteps); | ||
| 1165 | } | ||
| 1166 | } /* prepare printval */ | ||
| 1167 | |||
| 1168 | if (!this.gdes[i].strftm && (percent_s = this.gdes[i].format.indexOf('%S')) != -1) { | ||
| 1169 | if (magfact < 0.0) { | ||
| 1170 | //[printval, si_symb, magfact] = this.auto_scale(printval, si_symb, magfact); | ||
| 1171 | var dummy = this.auto_scale(printval, si_symb, magfact); printval = dummy[0]; si_symb = dummy[1]; magfact = dummy[2]; | ||
| 1172 | if (printval === 0.0) magfact = -1.0; | ||
| 1173 | } else { | ||
| 1174 | printval /= magfact; | ||
| 1175 | } | ||
| 1176 | this.gdes[i].format = this.gdes[i].format.substr(0, percent_s+1)+'s'+this.gdes[i].format.substr(percent_s+2); | ||
| 1177 | } else if (!this.gdes[i].strftm && this.gdes[i].format.indexOf('%s') != -1) { | ||
| 1178 | //[printval, si_symb, magfact] = this.auto_scale(printval, si_symb, magfact); | ||
| 1179 | var dummy = this.auto_scale(printval, si_symb, magfact); printval = dummy[0]; si_symb = dummy[1]; magfact = dummy[2]; | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | if (this.gdes[i].strftm) { | ||
| 1183 | this.gdes[i].legend = strftime(this.gdes[i].format, tmvdef); | ||
| 1184 | } else { | ||
| 1185 | this.gdes[i].legend = sprintf(this.gdes[i].format, printval, si_symb); | ||
| 1186 | } | ||
| 1187 | graphelement = 1; | ||
| 1188 | break; | ||
| 1189 | case RrdGraphDesc.GF_LINE: | ||
| 1190 | case RrdGraphDesc.GF_AREA: | ||
| 1191 | case RrdGraphDesc.GF_TICK: | ||
| 1192 | graphelement = 1; | ||
| 1193 | break; | ||
| 1194 | case RrdGraphDesc.GF_HRULE: | ||
| 1195 | if (isNaN(this.gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */ | ||
| 1196 | this.gdes[i].yrule = this.gdes[vidx].vf.val; | ||
| 1197 | }; | ||
| 1198 | graphelement = 1; | ||
| 1199 | break; | ||
| 1200 | case RrdGraphDesc.GF_VRULE: | ||
| 1201 | if (this.gdes[i].xrule === 0) { /* again ... the legend printer needs it */ | ||
| 1202 | this.gdes[i].xrule = this.gdes[vidx].vf.when; | ||
| 1203 | }; | ||
| 1204 | graphelement = 1; | ||
| 1205 | break; | ||
| 1206 | case RrdGraphDesc.GF_COMMENT: | ||
| 1207 | case RrdGraphDesc.GF_TEXTALIGN: | ||
| 1208 | case RrdGraphDesc.GF_DEF: | ||
| 1209 | case RrdGraphDesc.GF_CDEF: | ||
| 1210 | case RrdGraphDesc.GF_VDEF: | ||
| 1211 | case RrdGraphDesc.GF_SHIFT: | ||
| 1212 | case RrdGraphDesc.GF_XPORT: | ||
| 1213 | break; | ||
| 1214 | case RrdGraphDesc.GF_STACK: | ||
| 1215 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
| 1216 | break; | ||
| 1217 | } | ||
| 1218 | } | ||
| 1219 | return graphelement; | ||
| 1220 | }; | ||
| 1221 | |||
| 1222 | RrdGraph.prototype.reduce_data = function(gdes, cur_step) | ||
| 1223 | { | ||
| 1224 | var reduce_factor = Math.ceil(gdes.step / cur_step); | ||
| 1225 | var col, dst_row, row_cnt, start_offset, end_offset, skiprows = 0; | ||
| 1226 | var srcptr, dstptr; | ||
| 1227 | |||
| 1228 | gdes.step = cur_step * reduce_factor; /* set new step size for reduced data */ | ||
| 1229 | dstptr = 0; | ||
| 1230 | srcptr = 0; | ||
| 1231 | row_cnt = (gdes.end - gdes.start) / cur_step; | ||
| 1232 | |||
| 1233 | end_offset = gdes.end % gdes.step; | ||
| 1234 | start_offset = gdes.start % gdes.step; | ||
| 1235 | |||
| 1236 | if (start_offset) { | ||
| 1237 | gdes.start = gdes.start - start_offset; | ||
| 1238 | skiprows = reduce_factor - start_offset / cur_step; | ||
| 1239 | srcptr += skiprows * gdes.ds_cnt; | ||
| 1240 | for (col = 0; col < gdes.ds_cnt; col++) | ||
| 1241 | gdes.data[dstptr++] = Number.NaN; | ||
| 1242 | row_cnt -= skiprows; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | if (end_offset) { | ||
| 1246 | gdes.end = gdes.end - end_offset + gdes.step; | ||
| 1247 | skiprows = end_offset / cur_step; | ||
| 1248 | row_cnt -= skiprows; | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | if (row_cnt % reduce_factor) { | ||
| 1252 | throw new RrdGraphError("BUG in reduce_data(), SANITY CHECK: "+row_cnt+" rows cannot be reduced by "+reduce_factor); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | for (dst_row = 0; row_cnt >= reduce_factor; dst_row++) { | ||
| 1256 | for (col = 0; col < gdes.ds_cnt; col++) { | ||
| 1257 | var newval = Number.NaN; | ||
| 1258 | var validval = 0; | ||
| 1259 | |||
| 1260 | for (var i = 0; i < reduce_factor; i++) { | ||
| 1261 | if (isNaN(gdes.data[srcptr + i*gdes.ds_cnt + col])) continue; | ||
| 1262 | validval++; | ||
| 1263 | if (isNaN(newval)) { | ||
| 1264 | newval = gdes.data[srcptr + i * gdes.ds_cnt + col]; | ||
| 1265 | } else { | ||
| 1266 | switch (gdes.cf_reduce) { | ||
| 1267 | case RrdGraphDesc.CF_HWPREDICT: | ||
| 1268 | case RrdGraphDesc.CF_MHWPREDICT: | ||
| 1269 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
| 1270 | case RrdGraphDesc.CF_DEVPREDICT: | ||
| 1271 | case RrdGraphDesc.CF_SEASONAL: | ||
| 1272 | case RrdGraphDesc.CF_AVERAGE: | ||
| 1273 | newval += gdes.data[srcptr + i*gdes.ds_cnt + col]; | ||
| 1274 | break; | ||
| 1275 | case RrdGraphDesc.CF_MINIMUM: | ||
| 1276 | newval = Math.min(newval, gdes.data[srcptr + i*gdes.ds_cnt + col]); | ||
| 1277 | break; | ||
| 1278 | case RrdGraphDesc.CF_FAILURES: | ||
| 1279 | /* an interval contains a failure if any subintervals contained a failure */ | ||
| 1280 | case RrdGraphDesc.CF_MAXIMUM: | ||
| 1281 | newval = Math.max(newval, gdes.data[srcptr + i*gdes.ds_cnt + col]); | ||
| 1282 | break; | ||
| 1283 | case RrdGraphDesc.CF_LAST: | ||
| 1284 | newval = gdes.data[srcptr + i*gdes.ds_cnt + col]; | ||
| 1285 | break; | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | if (validval === 0) { | ||
| 1291 | newval = Number.NaN; | ||
| 1292 | } else { | ||
| 1293 | switch (gdes.cf_reduce) { | ||
| 1294 | case RrdGraphDesc.CF_HWPREDICT: | ||
| 1295 | case RrdGraphDesc.CF_MHWPREDICT: | ||
| 1296 | case RrdGraphDesc.CF_DEVSEASONAL: | ||
| 1297 | case RrdGraphDesc.CF_DEVPREDICT: | ||
| 1298 | case RrdGraphDesc.CF_SEASONAL: | ||
| 1299 | case RrdGraphDesc.CF_AVERAGE: | ||
| 1300 | newval /= validval; | ||
| 1301 | break; | ||
| 1302 | case RrdGraphDesc.CF_MINIMUM: | ||
| 1303 | case RrdGraphDesc.CF_FAILURES: | ||
| 1304 | case RrdGraphDesc.CF_MAXIMUM: | ||
| 1305 | case RrdGraphDesc.CF_LAST: | ||
| 1306 | break; | ||
| 1307 | } | ||
| 1308 | } | ||
| 1309 | gdes.data[dstptr++] = newval; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | srcptr += gdes.ds_cnt * reduce_factor; | ||
| 1313 | row_cnt -= reduce_factor; | ||
| 1314 | } | ||
| 1315 | if (end_offset) { | ||
| 1316 | for (col = 0; col < gdes.ds_cnt; col++) | ||
| 1317 | gdes.data[dstptr++] = Number.NaN; | ||
| 1318 | } | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | RrdGraph.prototype.data_fetch = function() | ||
| 1322 | { | ||
| 1323 | var skip; | ||
| 1324 | |||
| 1325 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 1326 | if (this.gdes[i].gf != RrdGraphDesc.GF_DEF) continue; | ||
| 1327 | |||
| 1328 | skip = false; | ||
| 1329 | for (var ii = 0; ii < i; ii++) { | ||
| 1330 | if (this.gdes[ii].gf != RrdGraphDesc.GF_DEF) continue; | ||
| 1331 | if ((this.gdes[i].rrd === this.gdes[ii].rrd) | ||
| 1332 | && (this.gdes[i].cf === this.gdes[ii].cf) | ||
| 1333 | && (this.gdes[i].cf_reduce === this.gdes[ii].cf_reduce) | ||
| 1334 | && (this.gdes[i].start_orig === this.gdes[ii].start_orig) | ||
| 1335 | && (this.gdes[i].end_orig === this.gdes[ii].end_orig) | ||
| 1336 | && (this.gdes[i].step_orig === this.gdes[ii].step_orig)) { | ||
| 1337 | this.gdes[i].start = this.gdes[ii].start; | ||
| 1338 | this.gdes[i].end = this.gdes[ii].end; | ||
| 1339 | this.gdes[i].step = this.gdes[ii].step; | ||
| 1340 | this.gdes[i].ds_cnt = this.gdes[ii].ds_cnt; | ||
| 1341 | this.gdes[i].ds_namv = this.gdes[ii].ds_namv; | ||
| 1342 | this.gdes[i].data = this.gdes[ii].data; | ||
| 1343 | this.gdes[i].data_first = 0; | ||
| 1344 | skip = true; | ||
| 1345 | } | ||
| 1346 | if (skip) break; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if (!skip) { | ||
| 1350 | var ft_step = this.gdes[i].step; /* ft_step will record what we got from fetch */ | ||
| 1351 | ft_step = this.data.fetch(this.gdes[i], ft_step); | ||
| 1352 | if (ft_step < 0) | ||
| 1353 | return -1; | ||
| 1354 | this.gdes[i].data_first = 1; | ||
| 1355 | // this.gdes[i].step = Math.max(this.gdes[i].step, this.step); // FIXME | ||
| 1356 | if (ft_step < this.gdes[i].step) { | ||
| 1357 | this.reduce_data(this.gdes[i], ft_step); | ||
| 1358 | } else { | ||
| 1359 | this.gdes[i].step = ft_step; | ||
| 1360 | } | ||
| 1361 | } | ||
| 1362 | /* lets see if the required data source is really there */ | ||
| 1363 | for (var ii = 0; ii < this.gdes[i].ds_cnt; ii++) { | ||
| 1364 | if (this.gdes[i].ds_namv[ii] === this.gdes[i].ds_nam) { | ||
| 1365 | this.gdes[i].ds = ii; | ||
| 1366 | break; | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | if (this.gdes[i].ds === -1) | ||
| 1371 | throw new RrdGraphError("No DS called '"+this.gdes[i].ds_nam+"' in '"+this.gdes[i].rrd+"'"); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | return 0; | ||
| 1375 | }; | ||
| 1376 | |||
| 1377 | RrdGraph.prototype.lcd = function (num) | ||
| 1378 | { | ||
| 1379 | var rest; | ||
| 1380 | for (var i = 0; num[i + 1] != 0; i++) { | ||
| 1381 | do { | ||
| 1382 | rest = num[i] % num[i + 1]; | ||
| 1383 | num[i] = num[i + 1]; | ||
| 1384 | num[i + 1] = rest; | ||
| 1385 | } while (rest != 0); // FIXME infinite loop ? | ||
| 1386 | num[i + 1] = num[i]; | ||
| 1387 | } | ||
| 1388 | return num[i]; | ||
| 1389 | }; | ||
| 1390 | |||
| 1391 | RrdGraph.prototype.data_calc = function() | ||
| 1392 | { | ||
| 1393 | var dataidx; | ||
| 1394 | var now; | ||
| 1395 | var rpnstack; | ||
| 1396 | |||
| 1397 | for (var gdi = 0, gdes_c = this.gdes.length; gdi < gdes_c; gdi++) { | ||
| 1398 | switch (this.gdes[gdi].gf) { | ||
| 1399 | case RrdGraphDesc.GF_XPORT: | ||
| 1400 | break; | ||
| 1401 | case RrdGraphDesc.GF_SHIFT: | ||
| 1402 | var vdp = this.gdes[this.gdes[gdi].vidx]; | ||
| 1403 | /* remove current shift */ | ||
| 1404 | vdp.start -= vdp.shift; | ||
| 1405 | vdp.end -= vdp.shift; | ||
| 1406 | |||
| 1407 | if (this.gdes[gdi].shidx >= 0) vdp.shift = this.gdes[this.gdes[gdi].shidx].vf.val; | ||
| 1408 | else vdp.shift = this.gdes[gdi].shval; | ||
| 1409 | |||
| 1410 | vdp.shift = (vdp.shift / vdp.step) * vdp.step; | ||
| 1411 | |||
| 1412 | vdp.start += vdp.shift; | ||
| 1413 | vdp.end += vdp.shift; | ||
| 1414 | break; | ||
| 1415 | case RrdGraphDesc.GF_VDEF: | ||
| 1416 | this.gdes[gdi].ds_cnt = 0; | ||
| 1417 | if (this.gdes[gdi].vf.calc(this.gdes[this.gdes[gdi].vidx])) | ||
| 1418 | throw new RrdGraphError("Error processing VDEF '"+this.gdes[gdi].vname+"'"); | ||
| 1419 | break; | ||
| 1420 | case RrdGraphDesc.GF_CDEF: | ||
| 1421 | this.gdes[gdi].ds_cnt = 1; | ||
| 1422 | this.gdes[gdi].ds = 0; | ||
| 1423 | this.gdes[gdi].data_first = 1; | ||
| 1424 | this.gdes[gdi].start = 0; | ||
| 1425 | this.gdes[gdi].end = 0; | ||
| 1426 | var steparray = []; | ||
| 1427 | var stepcnt = 0; | ||
| 1428 | dataidx = -1; | ||
| 1429 | |||
| 1430 | var rpnp = this.gdes[gdi].rpnp.rpnp; | ||
| 1431 | for (var rpi = 0; rpnp[rpi].op != RrdRpn.OP_END; rpi++) { | ||
| 1432 | if (rpnp[rpi].op === RrdRpn.OP_VARIABLE || rpnp[rpi].op === RrdRpn.OP_PREV_OTHER) { | ||
| 1433 | var ptr = rpnp[rpi].ptr; | ||
| 1434 | if (this.gdes[ptr].ds_cnt === 0) { /* this is a VDEF data source */ | ||
| 1435 | rpnp[rpi].val = this.gdes[ptr].vf.val; | ||
| 1436 | rpnp[rpi].op = RrdRpn.OP_NUMBER; | ||
| 1437 | } else { /* normal variables and PREF(variables) */ | ||
| 1438 | ++stepcnt; | ||
| 1439 | steparray[stepcnt - 1] = this.gdes[ptr].step; | ||
| 1440 | |||
| 1441 | if (this.gdes[gdi].start < this.gdes[ptr].start) | ||
| 1442 | this.gdes[gdi].start = this.gdes[ptr].start; | ||
| 1443 | if (this.gdes[gdi].end === 0 || this.gdes[gdi].end > this.gdes[ptr].end) | ||
| 1444 | this.gdes[gdi].end = this.gdes[ptr].end; | ||
| 1445 | |||
| 1446 | rpnp[rpi].data = this.gdes[ptr].data; | ||
| 1447 | rpnp[rpi].pdata = this.gdes[ptr].ds; | ||
| 1448 | rpnp[rpi].step = this.gdes[ptr].step; | ||
| 1449 | rpnp[rpi].ds_cnt = this.gdes[ptr].ds_cnt; | ||
| 1450 | } | ||
| 1451 | } | ||
| 1452 | } | ||
| 1453 | /* move the data pointers to the correct period */ | ||
| 1454 | for (var rpi = 0; rpnp[rpi].op != RrdRpn.OP_END; rpi++) { | ||
| 1455 | if (rpnp[rpi].op === RrdRpn.OP_VARIABLE || rpnp[rpi].op === RrdRpn.OP_PREV_OTHER) { | ||
| 1456 | var ptr = rpnp[rpi].ptr; | ||
| 1457 | var diff = this.gdes[gdi].start - this.gdes[ptr].start; | ||
| 1458 | |||
| 1459 | if (diff > 0) rpnp[rpi].pdata += (diff / this.gdes[ptr].step) * this.gdes[ptr].ds_cnt; | ||
| 1460 | } | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | if (steparray === null) { | ||
| 1464 | throw new RrdGraphError("rpn expressions without DEF or CDEF variables are not supported"); | ||
| 1465 | } | ||
| 1466 | steparray[stepcnt] = 0; | ||
| 1467 | this.gdes[gdi].step = this.lcd(steparray); | ||
| 1468 | this.gdes[gdi].data = []; | ||
| 1469 | |||
| 1470 | for (now = this.gdes[gdi].start + this.gdes[gdi].step; now <= this.gdes[gdi].end; now += this.gdes[gdi].step) { | ||
| 1471 | if (this.gdes[gdi].rpnp.calc(now, this.gdes[gdi].data, ++dataidx) === -1) | ||
| 1472 | return -1; | ||
| 1473 | } | ||
| 1474 | break; | ||
| 1475 | default: | ||
| 1476 | continue; | ||
| 1477 | } | ||
| 1478 | } | ||
| 1479 | return 0; | ||
| 1480 | }; | ||
| 1481 | |||
| 1482 | RrdGraph.prototype.data_proc = function() | ||
| 1483 | { | ||
| 1484 | var pixstep = (this.end - this.start) / this.xsize; | ||
| 1485 | var paintval; | ||
| 1486 | var minval = Number.NaN, maxval = Number.NaN; | ||
| 1487 | var gr_time; | ||
| 1488 | |||
| 1489 | /* memory for the processed data */ | ||
| 1490 | |||
| 1491 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 1492 | if ((this.gdes[i].gf === RrdGraphDesc.GF_LINE) || (this.gdes[i].gf === RrdGraphDesc.GF_AREA) || (this.gdes[i].gf === RrdGraphDesc.GF_TICK)) { | ||
| 1493 | this.gdes[i].p_data = []; | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | for (var i = 0; i < this.xsize; i++) { /* for each pixel */ | ||
| 1498 | var vidx; | ||
| 1499 | gr_time = this.start + pixstep * i; /* time of the current step */ | ||
| 1500 | paintval = 0.0; | ||
| 1501 | |||
| 1502 | for (var ii = 0 , gdes_c = this.gdes.length; ii < gdes_c; ii++) { | ||
| 1503 | var value; | ||
| 1504 | switch (this.gdes[ii].gf) { | ||
| 1505 | case RrdGraphDesc.GF_LINE: | ||
| 1506 | case RrdGraphDesc.GF_AREA: | ||
| 1507 | case RrdGraphDesc.GF_TICK: | ||
| 1508 | if (!this.gdes[ii].stack) paintval = 0.0; | ||
| 1509 | value = this.gdes[ii].yrule; | ||
| 1510 | if (isNaN(value) || (this.gdes[ii].gf === RrdGraphDesc.GF_TICK)) { | ||
| 1511 | vidx = this.gdes[ii].vidx; | ||
| 1512 | if (this.gdes[vidx].gf === RrdGraphDesc.GF_VDEF) { | ||
| 1513 | value = this.gdes[vidx].vf.val; | ||
| 1514 | } else if (gr_time >= this.gdes[vidx].start && gr_time < this.gdes[vidx].end) { | ||
| 1515 | value = this.gdes[vidx].data[Math.floor((gr_time - this.gdes[vidx].start) / this.gdes[vidx].step) * this.gdes[vidx].ds_cnt + this.gdes[vidx].ds]; | ||
| 1516 | } else { | ||
| 1517 | value = Number.NaN; | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | if (!isNaN(value)) { | ||
| 1521 | paintval += value; | ||
| 1522 | this.gdes[ii].p_data[i] = paintval; | ||
| 1523 | if (isFinite(paintval) && this.gdes[ii].gf != RrdGraphDesc.GF_TICK) { | ||
| 1524 | if ((isNaN(minval) || paintval < minval) && !(this.logarithmic && paintval <= 0.0)) | ||
| 1525 | minval = paintval; | ||
| 1526 | if (isNaN(maxval) || paintval > maxval) | ||
| 1527 | maxval = paintval; | ||
| 1528 | } | ||
| 1529 | } else { | ||
| 1530 | this.gdes[ii].p_data[i] = Number.NaN; | ||
| 1531 | } | ||
| 1532 | break; | ||
| 1533 | case RrdGraphDesc.GF_STACK: | ||
| 1534 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
| 1535 | break; | ||
| 1536 | default: | ||
| 1537 | break; | ||
| 1538 | } | ||
| 1539 | } | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | if (this.logarithmic) { | ||
| 1543 | if (isNaN(minval) || isNaN(maxval) || maxval <= 0) { | ||
| 1544 | minval = 0.0; | ||
| 1545 | maxval = 5.1; | ||
| 1546 | } | ||
| 1547 | if (minval <= 0) minval = maxval / 10e8; | ||
| 1548 | } else { | ||
| 1549 | if (isNaN(minval) || isNaN(maxval)) { | ||
| 1550 | minval = 0.0; | ||
| 1551 | maxval = 1.0; | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | if (isNaN(this.minval) || ((!this.rigid) && this.minval > minval)) { | ||
| 1556 | if (this.logarithmic) this.minval = minval / 2.0; | ||
| 1557 | else this.minval = minval; | ||
| 1558 | } | ||
| 1559 | if (isNaN(this.maxval) || (!this.rigid && this.maxval < maxval)) { | ||
| 1560 | if (this.logarithmic) this.maxval = maxval * 2.0; | ||
| 1561 | else this.maxval = maxval; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | if (this.minval > this.maxval) { | ||
| 1565 | if (this.minval > 0) this.minval = 0.99 * this.maxval; | ||
| 1566 | else this.minval = 1.01 * this.maxval; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | if (this.AlmostEqual2sComplement(this.minval, this.maxval, 4)) { | ||
| 1570 | if (this.maxval > 0) this.maxval *= 1.01; | ||
| 1571 | else this.maxval *= 0.99; | ||
| 1572 | if (this.AlmostEqual2sComplement(this.maxval, 0, 4)) this.maxval = 1.0; | ||
| 1573 | } | ||
| 1574 | return 0; | ||
| 1575 | }; | ||
| 1576 | |||
| 1577 | RrdGraph.prototype.leg_place = function (calc_width) | ||
| 1578 | { | ||
| 1579 | var interleg = this.TEXT.LEGEND.size * 1.5; | ||
| 1580 | var border = this.TEXT.LEGEND.size * 1.5; | ||
| 1581 | var fill = 0, fill_last; | ||
| 1582 | var legendwidth; // = this.ximg - 2 * border; | ||
| 1583 | var leg_c = 0; | ||
| 1584 | var leg_x = border; | ||
| 1585 | var leg_y = 0; //this.yimg; | ||
| 1586 | var leg_y_prev = 0; // this.yimg; | ||
| 1587 | var leg_cc; | ||
| 1588 | var glue = 0; | ||
| 1589 | var ii, mark = 0; | ||
| 1590 | var default_txtalign = RrdGraphDesc.TXA_JUSTIFIED; /*default line orientation */ | ||
| 1591 | var legspace; | ||
| 1592 | var tab; | ||
| 1593 | var saved_legend; | ||
| 1594 | |||
| 1595 | if(calc_width) legendwidth = 0; | ||
| 1596 | else legendwidth = this.legendwidth - 2 * border; | ||
| 1597 | |||
| 1598 | if (!this.no_legend && !this.only_graph) { | ||
| 1599 | legspace = []; | ||
| 1600 | for (var i = 0 , gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 1601 | var prt_fctn; /*special printfunctions */ | ||
| 1602 | if(calc_width) saved_legend = this.gdes[i].legend; | ||
| 1603 | fill_last = fill; | ||
| 1604 | if (this.gdes[i].gf === RrdGraphDesc.GF_TEXTALIGN) | ||
| 1605 | default_txtalign = this.gdes[i].txtalign; | ||
| 1606 | |||
| 1607 | if (!this.force_rules_legend) { | ||
| 1608 | if (this.gdes[i].gf === RrdGraphDesc.GF_HRULE && (this.gdes[i].yrule < this.minval || this.gdes[i].yrule > this.maxval)) | ||
| 1609 | this.gdes[i].legend = null; | ||
| 1610 | if (this.gdes[i].gf === RrdGraphDesc.GF_VRULE && (this.gdes[i].xrule < this.start || this.gdes[i].xrule > this.end)) | ||
| 1611 | this.gdes[i].legend = null; | ||
| 1612 | } | ||
| 1613 | this.gdes[i].legend = this.gdes[i].legend.replace(/\\t/gi, "\t") /* turn \\t into tab */ | ||
| 1614 | |||
| 1615 | leg_cc = this.gdes[i].legend.length; | ||
| 1616 | /* is there a controle code at the end of the legend string ? */ | ||
| 1617 | if (leg_cc >= 2 && this.gdes[i].legend.charAt(leg_cc - 2) === '\\') { | ||
| 1618 | prt_fctn = this.gdes[i].legend.charAt(leg_cc - 1); | ||
| 1619 | leg_cc -= 2; | ||
| 1620 | this.gdes[i].legend = this.gdes[i].legend.substr(0,leg_cc); | ||
| 1621 | } else { | ||
| 1622 | prt_fctn = null; | ||
| 1623 | } | ||
| 1624 | /* only valid control codes */ | ||
| 1625 | if (prt_fctn != 'l' && prt_fctn != 'n' && prt_fctn != 'r' && prt_fctn != 'j' && prt_fctn != 'c' && | ||
| 1626 | prt_fctn != '.' && prt_fctn != 'u' && prt_fctn != 's' && prt_fctn != null && prt_fctn != 'g') { | ||
| 1627 | throw new RrdGraphError("Unknown control code at the end of "+this.gdes[i].legend+": "+prt_fctn); | ||
| 1628 | } | ||
| 1629 | /* \n -> \l */ | ||
| 1630 | if (prt_fctn === 'n') prt_fctn = 'l'; | ||
| 1631 | if (prt_fctn === '.') prt_fctn = '\0'; | ||
| 1632 | |||
| 1633 | /* remove exess space from the end of the legend for \g */ | ||
| 1634 | while (prt_fctn === 'g' && leg_cc > 0 && this.gdes[i].legend.charAt(leg_cc - 1) === ' ') { | ||
| 1635 | leg_cc--; | ||
| 1636 | this.gdes[i].legend = this.gdes[i].legend.substr(0,leg_cc); | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | if (leg_cc != 0) { | ||
| 1640 | legspace[i] = (prt_fctn === 'g' ? 0 : interleg); | ||
| 1641 | if (fill > 0) fill += legspace[i]; | ||
| 1642 | fill += this.gfx.get_text_width(fill + border, this.TEXT.LEGEND, this.tabwidth, this.gdes[i].legend); | ||
| 1643 | leg_c++; | ||
| 1644 | } else { | ||
| 1645 | legspace[i] = 0; | ||
| 1646 | } | ||
| 1647 | /* who said there was a special tag ... ? */ | ||
| 1648 | if (prt_fctn === 'g') prt_fctn = null; | ||
| 1649 | |||
| 1650 | if (prt_fctn === null) { | ||
| 1651 | if(calc_width && (fill > legendwidth)) legendwidth = fill; | ||
| 1652 | |||
| 1653 | if (i === gdes_c - 1 || fill > legendwidth) { | ||
| 1654 | switch (default_txtalign) { | ||
| 1655 | case RrdGraphDesc.TXA_RIGHT: | ||
| 1656 | prt_fctn = 'r'; | ||
| 1657 | break; | ||
| 1658 | case RrdGraphDesc.TXA_CENTER: | ||
| 1659 | prt_fctn = 'c'; | ||
| 1660 | break; | ||
| 1661 | case RrdGraphDesc.TXA_JUSTIFIED: | ||
| 1662 | prt_fctn = 'j'; | ||
| 1663 | break; | ||
| 1664 | default: | ||
| 1665 | prt_fctn = 'l'; | ||
| 1666 | break; | ||
| 1667 | } | ||
| 1668 | } | ||
| 1669 | /* is it time to place the legends ? */ | ||
| 1670 | if (fill > legendwidth) { | ||
| 1671 | if (leg_c > 1) { /* go back one */ | ||
| 1672 | i--; | ||
| 1673 | fill = fill_last; | ||
| 1674 | leg_c--; | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | if (leg_c === 1 && prt_fctn === 'j') { | ||
| 1678 | prt_fctn = 'l'; | ||
| 1679 | } | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | if (prt_fctn != null) { | ||
| 1683 | leg_x = border; | ||
| 1684 | if (leg_c >= 2 && prt_fctn === 'j') { | ||
| 1685 | glue = (legendwidth - fill) / (leg_c - 1); | ||
| 1686 | } else { | ||
| 1687 | glue = 0; | ||
| 1688 | } | ||
| 1689 | if (prt_fctn === 'c') | ||
| 1690 | leg_x = border + (legendwidth - fill) / 2.0; | ||
| 1691 | if (prt_fctn === 'r') | ||
| 1692 | leg_x = legendwidth - fill + border; | ||
| 1693 | for (ii = mark; ii <= i; ii++) { | ||
| 1694 | if (this.gdes[ii].legend === '') continue; | ||
| 1695 | this.gdes[ii].leg_x = leg_x; | ||
| 1696 | this.gdes[ii].leg_y = leg_y + border; | ||
| 1697 | leg_x += this.gfx.get_text_width(leg_x, this.TEXT.LEGEND, this.tabwidth, this.gdes[ii].legend) + legspace[ii] + glue; | ||
| 1698 | } | ||
| 1699 | leg_y_prev = leg_y; | ||
| 1700 | if (leg_x > border || prt_fctn === 's') leg_y += this.TEXT.LEGEND.size * 1.4; | ||
| 1701 | if (prt_fctn === 's') leg_y -= this.TEXT.LEGEND.size; | ||
| 1702 | if (prt_fctn === 'u') leg_y -= this.TEXT.LEGEND.size * 1.4; | ||
| 1703 | |||
| 1704 | if(calc_width && (fill > legendwidth)) legendwidth = fill; | ||
| 1705 | fill = 0; | ||
| 1706 | leg_c = 0; | ||
| 1707 | mark = ii; | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | if(calc_width) this.gdes[i].legend = saved_legend; | ||
| 1711 | } | ||
| 1712 | if(calc_width) this.legendwidth = legendwidth + 2 * border; | ||
| 1713 | else this.legendheight = leg_y + border * 0.6; // FIXME 0.6 ?? | ||
| 1714 | } | ||
| 1715 | return 0; | ||
| 1716 | }; | ||
| 1717 | |||
| 1718 | RrdGraph.prototype.axis_paint = function() | ||
| 1719 | { | ||
| 1720 | this.gfx.line(this.xorigin - 4, this.yorigin, | ||
| 1721 | this.xorigin + this.xsize + 4, this.yorigin, | ||
| 1722 | this.MGRIDWIDTH, this.GRC.AXIS); | ||
| 1723 | |||
| 1724 | this.gfx.line(this.xorigin, this.yorigin + 4, | ||
| 1725 | this.xorigin, this.yorigin - this.ysize - 4, | ||
| 1726 | this.MGRIDWIDTH, this.GRC.AXIS); | ||
| 1727 | |||
| 1728 | this.gfx.new_area(this.xorigin + this.xsize + 2, this.yorigin - 3, | ||
| 1729 | this.xorigin + this.xsize + 2, | ||
| 1730 | this.yorigin + 3, this.xorigin + this.xsize + 7, this.yorigin, | ||
| 1731 | this.GRC.ARROW); | ||
| 1732 | this.gfx.close_path(); | ||
| 1733 | |||
| 1734 | this.gfx.new_area(this.xorigin - 3, this.yorigin - this.ysize - 2, | ||
| 1735 | this.xorigin + 3, this.yorigin - this.ysize - 2, | ||
| 1736 | this.xorigin, this.yorigin - this.ysize - 7, | ||
| 1737 | this.GRC.ARROW); | ||
| 1738 | this.gfx.close_path(); | ||
| 1739 | |||
| 1740 | if (this.second_axis_scale != 0){ | ||
| 1741 | this.gfx.line (this.xorigin+this.xsize,this.yorigin+4, | ||
| 1742 | this.xorigin+this.xsize,this.yorigin-this.ysize-4, | ||
| 1743 | MGRIDWIDTH, this.graph_col[this.GRC.AXIS]); | ||
| 1744 | this.gfx.new_area (this.xorigin+this.xsize-2, this.yorigin-this.ysize-2, | ||
| 1745 | this.xorigin+this.xsize+3, this.yorigin-this.ysize-2, | ||
| 1746 | this.xorigin+this.xsize, this.yorigin-this.ysize-7, /* LINEOFFSET */ | ||
| 1747 | this.GRC.ARROW); | ||
| 1748 | this.gfx.close_path(); | ||
| 1749 | } | ||
| 1750 | }; | ||
| 1751 | |||
| 1752 | RrdGraph.prototype.frexp10 = function (x) | ||
| 1753 | { | ||
| 1754 | var mnt; | ||
| 1755 | var iexp; | ||
| 1756 | |||
| 1757 | iexp = Math.floor(Math.log(Math.abs(x)) / Math.LN10); | ||
| 1758 | mnt = x / Math.pow(10.0, iexp); | ||
| 1759 | if (mnt >= 10.0) { | ||
| 1760 | iexp++; | ||
| 1761 | mnt = x / Math.pow(10.0, iexp); | ||
| 1762 | } | ||
| 1763 | return [mnt, iexp]; | ||
| 1764 | }; | ||
| 1765 | |||
| 1766 | RrdGraph.prototype.horizontal_log_grid = function () | ||
| 1767 | { | ||
| 1768 | var yloglab = [ [ 1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
| 1769 | [ 1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
| 1770 | [ 1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0 ], | ||
| 1771 | [ 1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0 ], | ||
| 1772 | [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10. ], | ||
| 1773 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] /* last line */ | ||
| 1774 | ]; | ||
| 1775 | var i, j, val_exp, min_exp; | ||
| 1776 | var nex; /* number of decades in data */ | ||
| 1777 | var logscale; /* scale in logarithmic space */ | ||
| 1778 | var exfrac = 1; /* decade spacing */ | ||
| 1779 | var mid = -1; /* row in yloglab for major grid */ | ||
| 1780 | var mspac; /* smallest major grid spacing (pixels) */ | ||
| 1781 | var flab; /* first value in yloglab to use */ | ||
| 1782 | var value, tmp, pre_value; | ||
| 1783 | var X0, X1, Y0; | ||
| 1784 | var graph_label; | ||
| 1785 | |||
| 1786 | nex = Math.log(this.maxval / this.minval)/Math.LN10; | ||
| 1787 | logscale = this.ysize / nex; | ||
| 1788 | /* major spacing for data with high dynamic range */ | ||
| 1789 | while (logscale * exfrac < 2.3 * this.TEXT.LEGEND.size) { // FIXME 3 -> 2.34 ?? | ||
| 1790 | if (exfrac === 1) exfrac = 2.3; // ??? 3 -> 2.34 | ||
| 1791 | else exfrac += 2.3 ; // 3-> 2.34 | ||
| 1792 | } | ||
| 1793 | /* major spacing for less dynamic data */ | ||
| 1794 | do { | ||
| 1795 | mid++; | ||
| 1796 | for (i = 0; yloglab[mid][i + 1] < 10.0; i++) {}; | ||
| 1797 | mspac = logscale * Math.log(10.0 / yloglab[mid][i])/Math.LN10; | ||
| 1798 | } while (mspac > 1.56 * this.TEXT.LEGEND.size && yloglab[mid][0] > 0); // FIXME 2->1.56 ?? | ||
| 1799 | if (mid) mid--; | ||
| 1800 | /* find first value in yloglab */ | ||
| 1801 | //for (flab = 0; yloglab[mid][flab] < 10 && this.frexp10(this.minval,tmp) > yloglab[mid][flab]; flab++); | ||
| 1802 | flab = -1; | ||
| 1803 | do { | ||
| 1804 | var ret; | ||
| 1805 | flab++; | ||
| 1806 | // [ret, tmp] = this.frexp10(this.minval); | ||
| 1807 | var dummy = this.frexp10(this.minval); ret = dummy[0]; tmp = dummy[1]; | ||
| 1808 | } while (yloglab[mid][flab] < 10 && ret > yloglab[mid][flab]); | ||
| 1809 | |||
| 1810 | if (yloglab[mid][flab] === 10.0) { | ||
| 1811 | tmp += 1.0; | ||
| 1812 | flab = 0; | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | val_exp = tmp; | ||
| 1816 | if (val_exp % exfrac) val_exp += Math.abs(-val_exp % exfrac); | ||
| 1817 | X0 = this.xorigin; | ||
| 1818 | X1 = this.xorigin + this.xsize; | ||
| 1819 | |||
| 1820 | /* draw grid */ | ||
| 1821 | pre_value = Number.NAN; | ||
| 1822 | |||
| 1823 | while (1) { | ||
| 1824 | value = yloglab[mid][flab] * Math.pow(10.0, val_exp); | ||
| 1825 | if (this.AlmostEqual2sComplement(value, pre_value, 4)) // FIXME | ||
| 1826 | break; /* it seems we are not converging */ | ||
| 1827 | pre_value = value; | ||
| 1828 | Y0 = this.ytr(value); | ||
| 1829 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) | ||
| 1830 | break; | ||
| 1831 | /* major grid line */ | ||
| 1832 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 1833 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 1834 | this.gfx.dashed_line(X0 - 2, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1835 | /* label */ | ||
| 1836 | if (this.force_units_si) { | ||
| 1837 | var scale; | ||
| 1838 | var pvalue; | ||
| 1839 | var symbol; | ||
| 1840 | |||
| 1841 | scale = Math.floor(val_exp / 3.0); | ||
| 1842 | if (value >= 1.0) pvalue = Math.pow(10.0, val_exp % 3); | ||
| 1843 | else pvalue = Math.pow(10.0, ((val_exp + 1) % 3) + 2); | ||
| 1844 | pvalue *= yloglab[mid][flab]; | ||
| 1845 | |||
| 1846 | if (((scale + this.si_symbcenter) < this.si_symbol.length) && ((scale + this.si_symbcenter) >= 0)) | ||
| 1847 | symbol = this.si_symbol[scale + this.si_symbcenter]; | ||
| 1848 | else | ||
| 1849 | symbol = '?'; | ||
| 1850 | graph_label = sprintf("%3.0f %s", pvalue, symbol); | ||
| 1851 | } else { | ||
| 1852 | graph_label = sprintf("%3.0e", value); | ||
| 1853 | } | ||
| 1854 | if (this.second_axis_scale != 0){ | ||
| 1855 | var graph_label_right; | ||
| 1856 | var sval = value*this.second_axis_scale+this.second_axis_shift; | ||
| 1857 | if (!this.second_axis_format[0]){ | ||
| 1858 | if (this.force_units_si) { | ||
| 1859 | var mfac = 1; | ||
| 1860 | var symb = ''; | ||
| 1861 | //[sval, symb, mfac ] = this.auto_scale(sval, symb, mfac); | ||
| 1862 | var dummy = this.auto_scale(sval, symb, mfac); sval = dummy[0]; symb = dummy[1]; mfac = dummy[2]; | ||
| 1863 | graph_label_right = sprintf("%4.0f %s", sval,symb); | ||
| 1864 | } else { | ||
| 1865 | graph_label_right = sprintf("%3.0e", sval); | ||
| 1866 | } | ||
| 1867 | } else { | ||
| 1868 | graph_label_right = sprintf(this.second_axis_format,sval,""); | ||
| 1869 | } | ||
| 1870 | this.gfx.text( X1+7, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth,0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_CENTER, graph_label_right ); | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | this.gfx.text(X0 - this.TEXT.AXIS.size, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth, 0.0, RrdGraph.GFX_H_RIGHT, RrdGraph.GFX_V_CENTER, graph_label); | ||
| 1874 | if (mid < 4 && exfrac === 1) { /* minor grid */ | ||
| 1875 | if (flab === 0) { /* find first and last minor line behind current major line * i is the first line and j tha last */ | ||
| 1876 | min_exp = val_exp - 1; | ||
| 1877 | for (i = 1; yloglab[mid][i] < 10.0; i++) {}; | ||
| 1878 | i = yloglab[mid][i - 1] + 1; | ||
| 1879 | j = 10; | ||
| 1880 | } else { | ||
| 1881 | min_exp = val_exp; | ||
| 1882 | i = yloglab[mid][flab - 1] + 1; | ||
| 1883 | j = yloglab[mid][flab]; | ||
| 1884 | } | ||
| 1885 | for (; i < j; i++) { /* draw minor lines below current major line */ | ||
| 1886 | value = i * Math.pow(10.0, min_exp); | ||
| 1887 | if (value < this.minval) continue; | ||
| 1888 | Y0 = this.ytr(value); | ||
| 1889 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
| 1890 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1891 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1892 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1893 | } | ||
| 1894 | } else if (exfrac > 1) { | ||
| 1895 | for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { | ||
| 1896 | value = Math.pow(10.0, i); | ||
| 1897 | if (value < this.minval) continue; | ||
| 1898 | Y0 = this.ytr(value); | ||
| 1899 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
| 1900 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1901 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1902 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1903 | } | ||
| 1904 | } | ||
| 1905 | if (yloglab[mid][++flab] === 10.0) { /* next decade */ | ||
| 1906 | flab = 0; | ||
| 1907 | val_exp += exfrac; | ||
| 1908 | } | ||
| 1909 | } | ||
| 1910 | if (mid < 4 && exfrac === 1) { /* draw minor lines after highest major line */ | ||
| 1911 | if (flab === 0) { /* find first and last minor line below current major line * i is the first line and j tha last */ | ||
| 1912 | min_exp = val_exp - 1; | ||
| 1913 | for (i = 1; yloglab[mid][i] < 10.0; i++) {}; | ||
| 1914 | i = yloglab[mid][i - 1] + 1; | ||
| 1915 | j = 10; | ||
| 1916 | } else { | ||
| 1917 | min_exp = val_exp; | ||
| 1918 | i = yloglab[mid][flab - 1] + 1; | ||
| 1919 | j = yloglab[mid][flab]; | ||
| 1920 | } | ||
| 1921 | for (; i < j; i++) { /* draw minor lines below current major line */ | ||
| 1922 | value = i * Math.pow(10.0, min_exp); | ||
| 1923 | if (value < this.minval) continue; | ||
| 1924 | Y0 = this.ytr(value); | ||
| 1925 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
| 1926 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1927 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1928 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1929 | } | ||
| 1930 | } else if (exfrac > 1) { /* fancy minor gridlines */ | ||
| 1931 | for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { | ||
| 1932 | value = Math.pow(10.0, i); | ||
| 1933 | if (value < this.minval) continue; | ||
| 1934 | Y0 = this.ytr(value); | ||
| 1935 | if (Math.floor(Y0 + 0.5) <= this.yorigin - this.ysize) break; | ||
| 1936 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1937 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1938 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1939 | } | ||
| 1940 | } | ||
| 1941 | return 1; | ||
| 1942 | }; | ||
| 1943 | |||
| 1944 | RrdGraph.prototype.vertical_grid = function() | ||
| 1945 | { | ||
| 1946 | var xlab_sel; /* which sort of label and grid ? */ | ||
| 1947 | var ti, tilab, timajor; | ||
| 1948 | var factor; | ||
| 1949 | var graph_label; | ||
| 1950 | var X0, Y0, Y1; /* points for filled graph and more */ | ||
| 1951 | |||
| 1952 | /* the type of time grid is determined by finding the number of seconds per pixel in the graph */ | ||
| 1953 | if (this.xlab_user.minsec === -1) { | ||
| 1954 | factor = (this.end - this.start) / this.xsize; | ||
| 1955 | xlab_sel = 0; | ||
| 1956 | |||
| 1957 | while (this.xlab[xlab_sel + 1].minsec != -1 && this.xlab[xlab_sel + 1].minsec <= factor) xlab_sel++; | ||
| 1958 | if (xlab_sel === 0) xlab_sel=1; // FIXME XXX XXX xlab_sel == 0 ??? | ||
| 1959 | while (this.xlab[xlab_sel - 1].minsec === this.xlab[xlab_sel].minsec && this.xlab[xlab_sel].length > (this.end - this.start)) xlab_sel--; | ||
| 1960 | this.xlab_user = this.xlab[xlab_sel]; | ||
| 1961 | |||
| 1962 | } | ||
| 1963 | Y0 = this.yorigin; | ||
| 1964 | Y1 = this.yorigin - this.ysize; | ||
| 1965 | |||
| 1966 | if (!(this.no_minor)) { | ||
| 1967 | for ( ti = this.find_first_time(this.start, this.xlab_user.gridtm, this.xlab_user.gridst), | ||
| 1968 | timajor = this.find_first_time(this.start, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
| 1969 | ti < this.end && ti != -1; | ||
| 1970 | ti = this.find_next_time(ti, this.xlab_user.gridtm, this.xlab_user.gridst)) { | ||
| 1971 | if (ti < this.start || ti > this.end) continue; | ||
| 1972 | while (timajor < ti && timajor != -1) timajor = this.find_next_time(timajor, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
| 1973 | if (timajor === -1) break; | ||
| 1974 | if (ti === timajor) continue; | ||
| 1975 | X0 = this.xtr(ti); | ||
| 1976 | this.gfx.line(X0, Y1 - 2, X0, Y1, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1977 | this.gfx.line(X0, Y0, X0, Y0 + 2, this.GRIDWIDTH, this.GRC.GRID); | ||
| 1978 | this.gfx.dashed_line(X0, Y0 + 1, X0, Y1 - 1, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1979 | } | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | for ( ti = this.find_first_time(this.start, this.xlab_user.mgridtm, this.xlab_user.mgridst); | ||
| 1983 | ti < this.end && ti != -1; | ||
| 1984 | ti = this.find_next_time(ti, this.xlab_user.mgridtm, this.xlab_user.mgridst) | ||
| 1985 | ) { | ||
| 1986 | if (ti < this.start || ti > this.end) continue; | ||
| 1987 | X0 = this.xtr(ti); | ||
| 1988 | this.gfx.line(X0, Y1 - 2, X0, Y1, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 1989 | this.gfx.line(X0, Y0, X0, Y0 + 3, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 1990 | this.gfx.dashed_line(X0, Y0 + 3, X0, Y1 - 2, this.MGRIDWIDTH, | ||
| 1991 | this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | for ( ti = this.find_first_time(this.start - this.xlab_user.precis / 2, this.xlab_user.labtm, this.xlab_user.labst); | ||
| 1995 | (ti <= this.end - this.xlab_user.precis / 2) && ti != -1; | ||
| 1996 | ti = this.find_next_time(ti, this.xlab_user.labtm, this.xlab_user.labst) | ||
| 1997 | ) { | ||
| 1998 | tilab = ti + this.xlab_user.precis / 2; | ||
| 1999 | if (tilab < this.start || tilab > this.end) | ||
| 2000 | continue; | ||
| 2001 | //localtime_r(&tilab, &tm); FIXME | ||
| 2002 | //strftime(graph_label, 99, this.xlab_user.stst, &tm); | ||
| 2003 | graph_label = strftime(this.xlab_user.stst, tilab); | ||
| 2004 | this.gfx.text(this.xtr(tilab), Y0 + 3, this.GRC.FONT, | ||
| 2005 | this.TEXT.AXIS, this.tabwidth, 0.0, | ||
| 2006 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_TOP, graph_label); | ||
| 2007 | } | ||
| 2008 | }; | ||
| 2009 | |||
| 2010 | RrdGraph.prototype.auto_scale = function (value, symb_ptr, magfact) | ||
| 2011 | { | ||
| 2012 | var sindex; | ||
| 2013 | |||
| 2014 | if (value === 0.0 || isNaN(value)) { | ||
| 2015 | sindex = 0; | ||
| 2016 | magfact = 1.0; | ||
| 2017 | } else { | ||
| 2018 | sindex = Math.floor((Math.log(Math.abs(value))/Math.LN10) / (Math.log(this.base)/Math.LN10)); | ||
| 2019 | magfact = Math.pow(this.base, sindex); | ||
| 2020 | value /= magfact; | ||
| 2021 | } | ||
| 2022 | if (sindex <= this.si_symbcenter && sindex >= -this.si_symbcenter) { | ||
| 2023 | symb_ptr = this.si_symbol[sindex + this.si_symbcenter]; | ||
| 2024 | } else { | ||
| 2025 | symb_ptr = '?'; | ||
| 2026 | } | ||
| 2027 | return [value, symb_ptr, magfact]; | ||
| 2028 | }; | ||
| 2029 | |||
| 2030 | RrdGraph.prototype.si_unit = function() | ||
| 2031 | { | ||
| 2032 | var digits; | ||
| 2033 | var viewdigits = 0; | ||
| 2034 | |||
| 2035 | digits = Math.floor(Math.log(Math.max(Math.abs(this.minval), Math.abs(this.maxval))) / Math.log(this.base)); | ||
| 2036 | |||
| 2037 | if (this.unitsexponent != 9999) { | ||
| 2038 | /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */ | ||
| 2039 | viewdigits = Math.floor(this.unitsexponent / 3); | ||
| 2040 | } else { | ||
| 2041 | viewdigits = digits; | ||
| 2042 | } | ||
| 2043 | |||
| 2044 | this.magfact = Math.pow(this.base, digits); | ||
| 2045 | |||
| 2046 | this.viewfactor = this.magfact / Math.pow(this.base, viewdigits); | ||
| 2047 | |||
| 2048 | if (((viewdigits + this.si_symbcenter) < this.si_symbol.length) && ((viewdigits + this.si_symbcenter) >= 0)) | ||
| 2049 | this.symbol = this.si_symbol[viewdigits + this.si_symbcenter]; | ||
| 2050 | else | ||
| 2051 | this.symbol = '?'; | ||
| 2052 | }; | ||
| 2053 | |||
| 2054 | RrdGraph.prototype.expand_range = function () | ||
| 2055 | { | ||
| 2056 | var sensiblevalues = [ 1000.0, 900.0, 800.0, 750.0, 700.0, 600.0, 500.0, 400.0, 300.0, 250.0, 200.0, 125.0, 100.0, 90.0, 80.0, 75.0, 70.0, 60.0, 50.0, 40.0, 30.0, 25.0, 20.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.8, 1.5, 1.2, 1.0, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1 ]; | ||
| 2057 | var scaled_min, scaled_max; | ||
| 2058 | var adj; | ||
| 2059 | var i; | ||
| 2060 | |||
| 2061 | if (isNaN(this.ygridstep)) { | ||
| 2062 | if (this.alt_autoscale) { | ||
| 2063 | var delt, fact; | ||
| 2064 | |||
| 2065 | delt = this.maxval - this.minval; | ||
| 2066 | adj = delt * 0.1; | ||
| 2067 | fact = 2.0 * Math.pow(10.0, Math.floor(Math.log(Math.max(Math.abs(this.minval), Math.abs(this.maxval)) / this.magfact)/Math.LN10) - 2); | ||
| 2068 | if (delt < fact) adj = (fact - delt) * 0.55; | ||
| 2069 | this.minval -= adj; | ||
| 2070 | this.maxval += adj; | ||
| 2071 | } else if (this.alt_autoscale_min) { | ||
| 2072 | adj = (this.maxval - this.minval) * 0.1; | ||
| 2073 | this.minval -= adj; | ||
| 2074 | } else if (this.alt_autoscale_max) { | ||
| 2075 | adj = (this.maxval - this.minval) * 0.1; | ||
| 2076 | this.maxval += adj; | ||
| 2077 | } else { | ||
| 2078 | scaled_min = this.minval / this.magfact; | ||
| 2079 | scaled_max = this.maxval / this.magfact; | ||
| 2080 | |||
| 2081 | for (i = 1; sensiblevalues[i] > 0; i++) { | ||
| 2082 | if (sensiblevalues[i - 1] >= scaled_min && sensiblevalues[i] <= scaled_min) | ||
| 2083 | this.minval = sensiblevalues[i] * this.magfact; | ||
| 2084 | if (-sensiblevalues[i - 1] <= scaled_min && -sensiblevalues[i] >= scaled_min) | ||
| 2085 | this.minval = -sensiblevalues[i - 1] * this.magfact; | ||
| 2086 | if (sensiblevalues[i - 1] >= scaled_max && sensiblevalues[i] <= scaled_max) | ||
| 2087 | this.maxval = sensiblevalues[i - 1] * this.magfact; | ||
| 2088 | if (-sensiblevalues[i - 1] <= scaled_max && -sensiblevalues[i] >= scaled_max) | ||
| 2089 | this.maxval = -sensiblevalues[i] * this.magfact; | ||
| 2090 | } | ||
| 2091 | } | ||
| 2092 | } else { | ||
| 2093 | this.minval = this.ylabfact * this.ygridstep * Math.floor(this.minval / (this.ylabfact * this.ygridstep)); | ||
| 2094 | this.maxval = this.ylabfact * this.ygridstep * Math.ceil(this.maxval / (this.ylabfact * this.ygridstep)); | ||
| 2095 | } | ||
| 2096 | }; | ||
| 2097 | |||
| 2098 | RrdGraph.prototype.calc_horizontal_grid = function() | ||
| 2099 | { | ||
| 2100 | var range; | ||
| 2101 | var scaledrange; | ||
| 2102 | var pixel, i; | ||
| 2103 | var gridind = 0; | ||
| 2104 | var decimals, fractionals; | ||
| 2105 | |||
| 2106 | this.ygrid_scale.labfact = 2; | ||
| 2107 | range = this.maxval - this.minval; | ||
| 2108 | scaledrange = range / this.magfact; | ||
| 2109 | if (isNaN(scaledrange) || !isFinite(scaledrange)) { | ||
| 2110 | return false; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | pixel = 1; | ||
| 2114 | if (isNaN(this.ygridstep)) { | ||
| 2115 | if (this.alt_ygrid) { | ||
| 2116 | decimals = Math.ceil(Math.log(Math.max(Math.abs(this.maxval), Math.abs(this.minval)) * this.viewfactor / this.magfact)/Math.LN10); | ||
| 2117 | if (decimals <= 0) decimals = 1; | ||
| 2118 | this.ygrid_scale.gridstep = Math.pow(10, Math.floor(Math.log(range * this.viewfactor / this.magfact)/Math.LN10)) / this.viewfactor * this.magfact; | ||
| 2119 | if (this.ygrid_scale.gridstep === 0) this.ygrid_scale.gridstep = 0.1; | ||
| 2120 | if (range / this.ygrid_scale.gridstep < 5 && this.ygrid_scale.gridstep >= 30) | ||
| 2121 | this.ygrid_scale.gridstep /= 10; | ||
| 2122 | if (range / this.ygrid_scale.gridstep > 15) | ||
| 2123 | this.ygrid_scale.gridstep *= 10; | ||
| 2124 | if (range / this.ygrid_scale.gridstep > 5) { | ||
| 2125 | this.ygrid_scale.labfact = 1; | ||
| 2126 | if (range / this.ygrid_scale.gridstep > 8 || this.ygrid_scale.gridstep < 1.8 * this.TEXT.AXIS.size) // 1.8 | ||
| 2127 | this.ygrid_scale.labfact = 2; | ||
| 2128 | } else { | ||
| 2129 | this.ygrid_scale.gridstep /= 5; | ||
| 2130 | this.ygrid_scale.labfact = 5; | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | fractionals = Math.floor(Math.log(this.ygrid_scale.gridstep * this.ygrid_scale.labfact * this.viewfactor / this.magfact)/Math.LN10); | ||
| 2134 | if (fractionals < 0) { /* small amplitude. */ | ||
| 2135 | var len = decimals - fractionals + 1; | ||
| 2136 | if (this.unitslength < len + 2) this.unitslength = len + 2; | ||
| 2137 | this.ygrid_scale.labfmt = sprintf("%%%d.%df%s", len, -fractionals, (this.symbol != ' ' ? " %s" : "")); | ||
| 2138 | } else { | ||
| 2139 | var len = decimals + 1; | ||
| 2140 | if (this.unitslength < len + 2) this.unitslength = len + 2; | ||
| 2141 | this.ygrid_scale.labfmt = sprintf("%%%d.0f%s", len, (this.symbol != ' ' ? " %s" : "")); | ||
| 2142 | } | ||
| 2143 | } else { /* classic rrd grid */ | ||
| 2144 | for (i = 0; this.ylab[i].grid > 0; i++) { | ||
| 2145 | pixel = this.ysize / (scaledrange / this.ylab[i].grid); | ||
| 2146 | gridind = i; | ||
| 2147 | if (pixel >= 5) break; | ||
| 2148 | } | ||
| 2149 | for (i = 0; i < 4; i++) { | ||
| 2150 | if (pixel * this.ylab[gridind].lfac[i] >= 1.8 * this.TEXT.AXIS.size) { // 1.8 | ||
| 2151 | this.ygrid_scale.labfact = this.ylab[gridind].lfac[i]; | ||
| 2152 | break; | ||
| 2153 | } | ||
| 2154 | } | ||
| 2155 | this.ygrid_scale.gridstep = this.ylab[gridind].grid * this.magfact; | ||
| 2156 | } | ||
| 2157 | } else { | ||
| 2158 | this.ygrid_scale.gridstep = this.ygridstep; | ||
| 2159 | this.ygrid_scale.labfact = this.ylabfact; | ||
| 2160 | } | ||
| 2161 | return true; | ||
| 2162 | }; | ||
| 2163 | |||
| 2164 | RrdGraph.prototype.draw_horizontal_grid = function() | ||
| 2165 | { | ||
| 2166 | var i; | ||
| 2167 | var scaledstep; | ||
| 2168 | var graph_label; | ||
| 2169 | var nlabels = 0; | ||
| 2170 | var X0 = this.xorigin; | ||
| 2171 | var X1 = this.xorigin + this.xsize; | ||
| 2172 | var sgrid = Math.round(this.minval / this.ygrid_scale.gridstep - 1); | ||
| 2173 | var egrid = Math.round(this.maxval / this.ygrid_scale.gridstep + 1); | ||
| 2174 | var MaxY; | ||
| 2175 | var second_axis_magfact = 0; | ||
| 2176 | var second_axis_symb = ''; | ||
| 2177 | var Y0, YN; | ||
| 2178 | var sisym; | ||
| 2179 | |||
| 2180 | scaledstep = this.ygrid_scale.gridstep / this.magfact * this.viewfactor; | ||
| 2181 | MaxY = scaledstep * egrid; | ||
| 2182 | for (i = sgrid; i <= egrid; i++) { | ||
| 2183 | Y0 = this.ytr(this.ygrid_scale.gridstep * i); | ||
| 2184 | YN = this.ytr(this.ygrid_scale.gridstep * (i + 1)); | ||
| 2185 | if (Math.floor(Y0 + 0.5) >= this.yorigin - this.ysize && Math.floor(Y0 + 0.5) <= this.yorigin) { | ||
| 2186 | if (i % this.ygrid_scale.labfact === 0 || (nlabels === 1 && (YN < this.yorigin - this.ysize || YN > this.yorigin))) { | ||
| 2187 | if (this.symbol === ' ') { | ||
| 2188 | if (this.alt_ygrid) { | ||
| 2189 | graph_label = sprintf(this.ygrid_scale.labfmt, scaledstep * i); // FIXME | ||
| 2190 | } else { | ||
| 2191 | if (MaxY < 10) { | ||
| 2192 | graph_label = sprintf("%4.1f", scaledstep * i); | ||
| 2193 | } else { | ||
| 2194 | graph_label = sprintf("%4.0f", scaledstep * i); | ||
| 2195 | } | ||
| 2196 | } | ||
| 2197 | } else { | ||
| 2198 | sisym = (i === 0 ? ' ' : this.symbol); | ||
| 2199 | if (this.alt_ygrid) { | ||
| 2200 | graph_label = sprintf(this.ygrid_scale.labfmt, scaledstep * i, sisym); | ||
| 2201 | } else { | ||
| 2202 | if (MaxY < 10) { | ||
| 2203 | graph_label = sprintf("%4.1f %s", scaledstep * i, sisym); | ||
| 2204 | } else { | ||
| 2205 | graph_label = sprintf("%4.0f %s", scaledstep * i, sisym); | ||
| 2206 | } | ||
| 2207 | } | ||
| 2208 | } | ||
| 2209 | nlabels++; | ||
| 2210 | if (this.second_axis_scale != 0){ | ||
| 2211 | var graph_label_right; | ||
| 2212 | sval = this.ygrid_scale.gridstep*i*this.second_axis_scale+this.second_axis_shift; | ||
| 2213 | if (!this.second_axis_format){ | ||
| 2214 | if (!second_axis_magfact){ | ||
| 2215 | var dummy = this.ygrid_scale.gridstep*(sgrid+egrid)/2.0*this.second_axis_scale+this.second_axis_shift; | ||
| 2216 | //[dummy, second_axis_symb, second_axis_magfact ] = this.auto_scale(dummy,second_axis_symb,second_axis_magfact); | ||
| 2217 | dummy = this.auto_scale(dummy,second_axis_symb,second_axis_magfact); second_axis_symb = dummy[1]; second_axis_magfact=dummy[2]; | ||
| 2218 | } | ||
| 2219 | sval /= second_axis_magfact; | ||
| 2220 | if(MaxY < 10) { | ||
| 2221 | graph_label_right = sprintf("%5.1f %s", sval, second_axis_symb); | ||
| 2222 | } else { | ||
| 2223 | graph_label_right = sprintf("%5.0f %s", sval, second_axis_symb); | ||
| 2224 | } | ||
| 2225 | } else { | ||
| 2226 | graph_label_right = sprintf(this.second_axis_format, sval); | ||
| 2227 | } | ||
| 2228 | this.gfx.text (X1+7, Y0, this.GRC.FONT, this.TEXT.AXIS, this.tabwidth, 0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_CENTER, graph_label_right ); | ||
| 2229 | } | ||
| 2230 | this.gfx.text(X0 - this.TEXT.AXIS.size , Y0, this.GRC.FONT, this.TEXT.AXIS , this.tabwidth, 0.0, RrdGraph.GFX_H_RIGHT, RrdGraph.GFX_V_CENTER, graph_label); | ||
| 2231 | this.gfx.line(X0 - 2, Y0, X0, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 2232 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID); | ||
| 2233 | this.gfx.dashed_line(X0 - 2, Y0, X1 + 2, Y0, this.MGRIDWIDTH, this.GRC.MGRID, this.grid_dash_on, this.grid_dash_off); | ||
| 2234 | } else if (!this.no_minor) { | ||
| 2235 | this.gfx.line( X0 - 2, Y0, X0, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 2236 | this.gfx.line(X1, Y0, X1 + 2, Y0, this.GRIDWIDTH, this.GRC.GRID); | ||
| 2237 | this.gfx.dashed_line(X0 - 1, Y0, X1 + 1, Y0, this.GRIDWIDTH, this.GRC.GRID, this.grid_dash_on, this.grid_dash_off); | ||
| 2238 | } | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | return 1; | ||
| 2242 | }; | ||
| 2243 | |||
| 2244 | RrdGraph.prototype.grid_paint = function() | ||
| 2245 | { | ||
| 2246 | var i; | ||
| 2247 | var res = 0; | ||
| 2248 | var X0, Y0; | ||
| 2249 | |||
| 2250 | if (this.draw_3d_border > 0) { | ||
| 2251 | i = this.draw_3d_border; | ||
| 2252 | this.gfx.new_area(0, this.yimg, i, this.yimg - i, i, i, this.GRC.SHADEA); | ||
| 2253 | this.gfx.add_point(this.ximg - i, i); | ||
| 2254 | this.gfx.add_point(this.ximg, 0); | ||
| 2255 | this.gfx.add_point(0, 0); | ||
| 2256 | this.gfx.close_path(); | ||
| 2257 | this.gfx.new_area(i, this.yimg - i, this.ximg - i, this.yimg - i, this.ximg - i, i, this.GRC.SHADEB); | ||
| 2258 | this.gfx.add_point(this.ximg, 0); | ||
| 2259 | this.gfx.add_point(this.ximg, this.yimg); | ||
| 2260 | this.gfx.add_point(0, this.yimg); | ||
| 2261 | this.gfx.close_path(); | ||
| 2262 | } | ||
| 2263 | if (this.draw_x_grid) | ||
| 2264 | this.vertical_grid(); | ||
| 2265 | if (this.draw_y_grid) { | ||
| 2266 | if (this.logarithmic) | ||
| 2267 | res = this.horizontal_log_grid(); | ||
| 2268 | else | ||
| 2269 | res = this.draw_horizontal_grid(); | ||
| 2270 | /* dont draw horizontal grid if there is no min and max val */ | ||
| 2271 | if (!res) { | ||
| 2272 | this.gfx.text(this.ximg / 2, (2 * this.yorigin - this.ysize) / 2, | ||
| 2273 | this.GRC.FONT, this.TEXT.AXIS, | ||
| 2274 | this.tabwidth, 0.0, | ||
| 2275 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, 'No Data found'); | ||
| 2276 | } | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | /* yaxis unit description */ | ||
| 2280 | if (this.ylegend){ | ||
| 2281 | this.gfx.text(this.xOriginLegendY+10, this.yOriginLegendY, | ||
| 2282 | this.GRC.FONT, this.TEXT.UNIT, this.tabwidth, this.YLEGEND_ANGLE, | ||
| 2283 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, this.ylegend); | ||
| 2284 | |||
| 2285 | } | ||
| 2286 | if (this.second_axis_legend){ | ||
| 2287 | this.gfx.text(this.xOriginLegendY2+10, this.yOriginLegendY2, | ||
| 2288 | this.GRC.FONT, this.TEXT.UNIT, this.tabwidth, this.YLEGEND_ANGLE, | ||
| 2289 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_CENTER, this.second_axis_legend); | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | /* graph title */ | ||
| 2293 | this.gfx.text(this.xOriginTitle, this.yOriginTitle+6, | ||
| 2294 | this.GRC.FONT, this.TEXT.TITLE, this.tabwidth, 0.0, RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_TOP, this.title); | ||
| 2295 | /* rrdtool 'logo' */ | ||
| 2296 | if (!this.no_rrdtool_tag){ | ||
| 2297 | var color = this.parse_color(this.GRC.FONT); | ||
| 2298 | color[3] = 0.3; | ||
| 2299 | var water_color = this.color2rgba(color); | ||
| 2300 | var xpos = this.legendposition === RrdGraph.LEGEND_POS_EAST ? this.xOriginLegendY : this.ximg - 4; | ||
| 2301 | this.gfx.text(xpos, 5, water_color, this.TEXT.WATERMARK, this.tabwidth, | ||
| 2302 | -90, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); | ||
| 2303 | } | ||
| 2304 | /* graph watermark */ | ||
| 2305 | if (this.watermark) { | ||
| 2306 | var color = this.parse_color(this.GRC.FONT) | ||
| 2307 | color[3] = 0.3; | ||
| 2308 | var water_color = this.color2rgba(color); | ||
| 2309 | this.gfx.text(this.ximg / 2, this.yimg - 6, water_color, this.TEXT.WATERMARK , this.tabwidth, 0, | ||
| 2310 | RrdGraph.GFX_H_CENTER, RrdGraph.GFX_V_BOTTOM, this.watermark); | ||
| 2311 | } | ||
| 2312 | /* graph labels */ | ||
| 2313 | if (!(this.no_legend) && !(this.only_graph)) { | ||
| 2314 | for (var i = 0 , gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 2315 | if (!this.gdes[i].legend) continue; | ||
| 2316 | X0 = this.xOriginLegend + this.gdes[i].leg_x; | ||
| 2317 | Y0 = this.legenddirection === RrdGraph.LEGEND_DIR_TOP_DOWN ? this.yOriginLegend + this.gdes[i].leg_y : this.yOriginLegend + this.legendheight - this.gdes[i].leg_y; | ||
| 2318 | this.gfx.text(X0, Y0, this.GRC.FONT, this.TEXT.LEGEND, this.tabwidth, 0.0, RrdGraph.GFX_H_LEFT, RrdGraph.GFX_V_BOTTOM, this.gdes[i].legend); | ||
| 2319 | if (this.gdes[i].gf != RrdGraphDesc.GF_PRINT && this.gdes[i].gf != RrdGraphDesc.GF_GPRINT && this.gdes[i].gf != RrdGraphDesc.GF_COMMENT) { | ||
| 2320 | var boxH, boxV; | ||
| 2321 | var X1, Y1; | ||
| 2322 | |||
| 2323 | boxH = this.gfx.get_text_width(0,this.TEXT.LEGEND, this.tabwidth, 'o') * 1.2; | ||
| 2324 | boxV = boxH; | ||
| 2325 | |||
| 2326 | Y0 -= boxV * 0.4; | ||
| 2327 | |||
| 2328 | if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_HRULE) { | ||
| 2329 | this.gfx.line(X0, Y0 - boxV / 2, X0 + boxH, Y0 - boxV / 2, 1.0, this.gdes[i].col); | ||
| 2330 | } else if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_VRULE) { | ||
| 2331 | this.gfx.line(X0 + boxH / 2, Y0, X0 + boxH / 2, Y0 - boxV, 1.0, this.gdes[i].col); | ||
| 2332 | } else if (this.dynamic_labels && this.gdes[i].gf === RrdGraphDesc.GF_LINE) { | ||
| 2333 | this.gfx.line(X0, Y0, X0 + boxH, Y0 - boxV, this.gdes[i].linewidth, this.gdes[i].col); | ||
| 2334 | } else { | ||
| 2335 | this.gfx.new_area(X0, Y0 - boxV, X0, Y0, X0 + boxH, Y0, this.GRC.BACK); | ||
| 2336 | this.gfx.add_point(X0 + boxH, Y0 - boxV); | ||
| 2337 | this.gfx.close_path(); | ||
| 2338 | this.gfx.new_area(X0, Y0 - boxV, X0, Y0, X0 + boxH, Y0, this.gdes[i].col); | ||
| 2339 | this.gfx.add_point(X0 + boxH, Y0 - boxV); | ||
| 2340 | this.gfx.close_path(); | ||
| 2341 | if (this.gdes[i].dash) this.gfx.set_dash([ 3.0 ], 1, 0.0); | ||
| 2342 | this.gfx.rectangle(X0, Y0, X0 + boxH, Y0 - boxV, 1.0, this.GRC.FRAME); | ||
| 2343 | } | ||
| 2344 | } | ||
| 2345 | } | ||
| 2346 | } | ||
| 2347 | }; | ||
| 2348 | |||
| 2349 | RrdGraph.prototype.graph_size_location = function (elements) | ||
| 2350 | { | ||
| 2351 | var Xvertical = 0; | ||
| 2352 | var Xvertical2 = 0; | ||
| 2353 | var Ytitle = 0; | ||
| 2354 | var Xylabel = 0; | ||
| 2355 | var Xmain = 0; | ||
| 2356 | var Ymain = 0; | ||
| 2357 | var Yxlabel = 0; | ||
| 2358 | var Xspacing = 15; | ||
| 2359 | var Yspacing = 15; | ||
| 2360 | var Ywatermark = 4; | ||
| 2361 | |||
| 2362 | if (this.only_graph) { | ||
| 2363 | this.xorigin = 0; | ||
| 2364 | this.ximg = this.xsize; | ||
| 2365 | this.yimg = this.ysize; | ||
| 2366 | this.yorigin = this.ysize; | ||
| 2367 | this.xtr(0); | ||
| 2368 | this.ytr(Number.NaN); | ||
| 2369 | return 0; | ||
| 2370 | } | ||
| 2371 | |||
| 2372 | if(this.watermark) | ||
| 2373 | Ywatermark = this.TEXT.WATERMARK.size * 1.5; // 2 | ||
| 2374 | if(this.ylegend) | ||
| 2375 | Xvertical = this.TEXT.UNIT.size * 1.5; // 2 | ||
| 2376 | if(this.second_axis_legend) { | ||
| 2377 | Xvertical2 = this.TEXT.UNIT.size * 1.5; // 2 | ||
| 2378 | } else { | ||
| 2379 | Xvertical2 = Xspacing; | ||
| 2380 | } | ||
| 2381 | |||
| 2382 | if(this.title) | ||
| 2383 | Ytitle = this.TEXT.TITLE.size * 1.95 + 10; // 2.6 | ||
| 2384 | else | ||
| 2385 | Ytitle = Yspacing; | ||
| 2386 | |||
| 2387 | if (elements) { | ||
| 2388 | if (this.draw_x_grid) | ||
| 2389 | Yxlabel = this.TEXT.AXIS.size * 1.35; // 2.5 1.87 | ||
| 2390 | if (this.draw_y_grid || this.forceleftspace) // FIXME | ||
| 2391 | Xylabel = this.gfx.get_text_width(0, this.TEXT.AXIS, this.tabwidth, '0') * this.unitslength; | ||
| 2392 | } | ||
| 2393 | Xylabel += Xspacing; | ||
| 2394 | this.legendheight = 0; | ||
| 2395 | this.legendwidth = 0; | ||
| 2396 | if(!this.no_legend) { | ||
| 2397 | if(this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST){ | ||
| 2398 | if (this.leg_place(1) === -1) return -1; // FIXME | ||
| 2399 | } | ||
| 2400 | } | ||
| 2401 | |||
| 2402 | if(this.full_size_mode) { | ||
| 2403 | this.ximg = this.xsize; | ||
| 2404 | this.yimg = this.ysize; | ||
| 2405 | Xmain = this.ximg; | ||
| 2406 | Ymain = this.yimg; | ||
| 2407 | |||
| 2408 | Xmain -= Xylabel;// + Xspacing; | ||
| 2409 | if((this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST) && !(this.no_legend) ) | ||
| 2410 | Xmain -= this.legendwidth;// + Xspacing; | ||
| 2411 | if (this.second_axis_scale != 0) Xmain -= Xylabel; | ||
| 2412 | if (!(this.no_rrdtool_tag)) Xmain -= Xspacing; | ||
| 2413 | |||
| 2414 | Xmain -= Xvertical + Xvertical2; | ||
| 2415 | |||
| 2416 | if(Xmain < 1) Xmain = 1; | ||
| 2417 | this.xsize = Xmain; | ||
| 2418 | |||
| 2419 | if (!(this.no_legend)) { | ||
| 2420 | if(this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH){ | ||
| 2421 | this.legendwidth = this.ximg; | ||
| 2422 | if (this.leg_place(0) === -1) return -1; | ||
| 2423 | } | ||
| 2424 | } | ||
| 2425 | |||
| 2426 | if( (this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH) && !(this.no_legend) ) | ||
| 2427 | Ymain -= Yxlabel + this.legendheight; | ||
| 2428 | else Ymain -= Yxlabel; | ||
| 2429 | |||
| 2430 | Ymain -= Ytitle; | ||
| 2431 | |||
| 2432 | if (this.nolegened) Ymain -= 0.5*Yspacing; | ||
| 2433 | if (this.watermark) Ymain -= Ywatermark; | ||
| 2434 | if(Ymain < 1) Ymain = 1; | ||
| 2435 | this.ysize = Ymain; | ||
| 2436 | } else { | ||
| 2437 | if (elements) { | ||
| 2438 | // Xmain = this.xsize; // + Xspacing; | ||
| 2439 | Xmain = this.xsize + Xspacing; //FIXME ??? | ||
| 2440 | Ymain = this.ysize; | ||
| 2441 | } | ||
| 2442 | this.ximg = Xmain + Xylabel; | ||
| 2443 | if (!this.no_rrdtool_tag) this.ximg += Xspacing; | ||
| 2444 | |||
| 2445 | if( (this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST) && !this.no_legend ) | ||
| 2446 | this.ximg += this.legendwidth;// + Xspacing; | ||
| 2447 | if (this.second_axis_scale != 0) this.ximg += Xylabel; | ||
| 2448 | |||
| 2449 | this.ximg += Xvertical + Xvertical2; | ||
| 2450 | |||
| 2451 | if (!(this.no_legend)) { | ||
| 2452 | if(this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH){ | ||
| 2453 | this.legendwidth = this.ximg; | ||
| 2454 | if (this.leg_place(0) === -1) return -1; | ||
| 2455 | } | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | this.yimg = Ymain + Yxlabel; | ||
| 2459 | if( (this.legendposition === RrdGraph.LEGEND_POS_NORTH || this.legendposition === RrdGraph.LEGEND_POS_SOUTH) && !(this.no_legend) ) | ||
| 2460 | this.yimg += this.legendheight; | ||
| 2461 | |||
| 2462 | if (Ytitle) this.yimg += Ytitle; | ||
| 2463 | else this.yimg += 1.5 * Yspacing; | ||
| 2464 | |||
| 2465 | if (this.no_legend) this.yimg += 0.5*Yspacing; | ||
| 2466 | if (this.watermark) this.yimg += Ywatermark; | ||
| 2467 | } | ||
| 2468 | |||
| 2469 | |||
| 2470 | if (!this.no_legend) { | ||
| 2471 | if(this.legendposition === RrdGraph.LEGEND_POS_WEST || this.legendposition === RrdGraph.LEGEND_POS_EAST){ | ||
| 2472 | if (this.leg_place(0) === -1) return -1; | ||
| 2473 | } | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | switch(this.legendposition){ | ||
| 2477 | case RrdGraph.LEGEND_POS_NORTH: | ||
| 2478 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
| 2479 | this.yOriginTitle = 0; | ||
| 2480 | this.xOriginLegend = 0; | ||
| 2481 | this.yOriginLegend = Math.round(Ytitle); | ||
| 2482 | this.xOriginLegendY = 0; | ||
| 2483 | this.yOriginLegendY = Math.round(Ytitle + this.legendheight + (Ymain / 2) + Yxlabel); | ||
| 2484 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
| 2485 | this.yorigin = Math.round(Ytitle + this.legendheight + Ymain); | ||
| 2486 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
| 2487 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
| 2488 | this.yOriginLegendY2 = Math.round(Ytitle + this.legendheight + (Ymain / 2) + Yxlabel); | ||
| 2489 | break; | ||
| 2490 | case RrdGraph.LEGEND_POS_WEST: | ||
| 2491 | this.xOriginTitle = Math.round(this.legendwidth + this.xsize / 2); | ||
| 2492 | this.yOriginTitle = 0; | ||
| 2493 | this.xOriginLegend = 0; | ||
| 2494 | this.yOriginLegend = Math.round(Ytitle); | ||
| 2495 | this.xOriginLegendY = Math.round(this.legendwidth); | ||
| 2496 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
| 2497 | this.xorigin = Math.round(this.legendwidth + Xvertical + Xylabel); | ||
| 2498 | this.yorigin = Math.round(Ytitle + Ymain); | ||
| 2499 | this.xOriginLegendY2 = Math.round(this.legendwidth + Xvertical + Xylabel + Xmain); | ||
| 2500 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
| 2501 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
| 2502 | break; | ||
| 2503 | case RrdGraph.LEGEND_POS_SOUTH: | ||
| 2504 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
| 2505 | this.yOriginTitle = 0; | ||
| 2506 | this.xOriginLegend = 0; | ||
| 2507 | this.yOriginLegend = Math.round(Ytitle + Ymain + Yxlabel); | ||
| 2508 | this.xOriginLegendY = 0; | ||
| 2509 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
| 2510 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
| 2511 | this.yorigin = Math.round(Ytitle + Ymain); | ||
| 2512 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
| 2513 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
| 2514 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
| 2515 | break; | ||
| 2516 | case RrdGraph.LEGEND_POS_EAST: | ||
| 2517 | this.xOriginTitle = Math.round(this.xsize / 2); | ||
| 2518 | this.yOriginTitle = 0; | ||
| 2519 | this.xOriginLegend = Math.round(Xvertical + Xylabel + Xmain + Xvertical2); | ||
| 2520 | if (this.second_axis_scale != 0) this.xOriginLegend += Xylabel; | ||
| 2521 | this.yOriginLegend = Math.round(Ytitle); | ||
| 2522 | this.xOriginLegendY = 0; | ||
| 2523 | this.yOriginLegendY = Math.round(Ytitle + (Ymain / 2)); | ||
| 2524 | this.xorigin = Math.round(Xvertical + Xylabel); | ||
| 2525 | this.yorigin = Math.round(Ytitle + Ymain); | ||
| 2526 | this.xOriginLegendY2 = Math.round(Xvertical + Xylabel + Xmain); | ||
| 2527 | if (this.second_axis_scale != 0) this.xOriginLegendY2 += Xylabel; | ||
| 2528 | this.yOriginLegendY2 = Math.round(Ytitle + (Ymain / 2)); | ||
| 2529 | |||
| 2530 | if (!this.no_rrdtool_tag){ | ||
| 2531 | this.xOriginTitle += Xspacing; | ||
| 2532 | this.xOriginLegend += Xspacing; | ||
| 2533 | this.xOriginLegendY += Xspacing; | ||
| 2534 | this.xorigin += Xspacing; | ||
| 2535 | this.xOriginLegendY2 += Xspacing; | ||
| 2536 | } | ||
| 2537 | break; | ||
| 2538 | } | ||
| 2539 | this.xtr(0); | ||
| 2540 | this.ytr(Number.NaN); | ||
| 2541 | return 0; | ||
| 2542 | }; | ||
| 2543 | |||
| 2544 | RrdGraph.prototype.graph_paint = function() | ||
| 2545 | { | ||
| 2546 | if (this.logarithmic && this.minval <= 0) | ||
| 2547 | throw new RrdGraphError("for a logarithmic yaxis you must specify a lower-limit > 0"); | ||
| 2548 | |||
| 2549 | //var start_end = RrdTime.proc_start_end(this.start_t, this.end_t); | ||
| 2550 | //this.start = start_end[0]; | ||
| 2551 | //this.end = start_end[1]; | ||
| 2552 | |||
| 2553 | if (this.start < 3600 * 24 * 365 * 10) | ||
| 2554 | throw new RrdGraphError("the first entry to fetch should be after 1980 ("+this.start+")"); | ||
| 2555 | |||
| 2556 | if (this.end < this.start) | ||
| 2557 | throw new RrdGraphError("start ("+this.start+") should be less than end ("+this.end+")"); | ||
| 2558 | |||
| 2559 | //this.xlab_form = null | ||
| 2560 | this.xlab_user = { minsec: -1, length: 0, gridtm: 0, gridst: 0, mgridtm: 0, mgridst: 0, labtm: 0, labst: 0, precis: 0, stst: null }; | ||
| 2561 | this.ygrid_scale = { gridstep: 0.0, labfact:0 , labfmt: null }; | ||
| 2562 | this.minval = this.setminval; | ||
| 2563 | this.maxval = this.setmaxval; | ||
| 2564 | |||
| 2565 | this.step = Math.max(this.step, (this.end - this.start) / this.xsize); | ||
| 2566 | |||
| 2567 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 2568 | this.gdes[i].step = 0; // FIXME 0? | ||
| 2569 | this.gdes[i].step_orig = this.step; | ||
| 2570 | this.gdes[i].start = this.start; // FIXME SHIFT | ||
| 2571 | // this.gdes[i].start_orig = this.start; | ||
| 2572 | this.gdes[i].end = this.end; // FIXME SHIFT | ||
| 2573 | // this.gdes[i].end_orig = this.end; | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | var areazero = 0.0 | ||
| 2577 | var lastgdes = null; | ||
| 2578 | |||
| 2579 | if (this.data_fetch() === -1) | ||
| 2580 | return -1; | ||
| 2581 | if (this.data_calc() === -1) | ||
| 2582 | return -1; | ||
| 2583 | var i = this.print_calc(); | ||
| 2584 | if (i < 0) | ||
| 2585 | return -1; | ||
| 2586 | if (this.graph_size_location(i) === -1) | ||
| 2587 | return -1; | ||
| 2588 | |||
| 2589 | if (this.data_proc() === -1) | ||
| 2590 | return -1; | ||
| 2591 | if (!this.logarithmic) | ||
| 2592 | this.si_unit(); | ||
| 2593 | if (!this.rigid && !this.logarithmic) | ||
| 2594 | this.expand_range(); | ||
| 2595 | |||
| 2596 | if (this.magfact === 0) this.magfact =1; // FIXME logarithmic ¿? | ||
| 2597 | |||
| 2598 | if (!this.calc_horizontal_grid()) | ||
| 2599 | return -1; | ||
| 2600 | |||
| 2601 | this.ytr(Number.NaN); | ||
| 2602 | |||
| 2603 | this.gfx.size(this.ximg, this.yimg); | ||
| 2604 | |||
| 2605 | this.gfx.new_area(0, 0, 0, this.yimg, this.ximg, this.yimg, this.GRC.BACK); | ||
| 2606 | this.gfx.add_point(this.ximg, 0); | ||
| 2607 | this.gfx.close_path(); | ||
| 2608 | |||
| 2609 | this.gfx.new_area(this.xorigin, this.yorigin, this.xorigin + this.xsize, | ||
| 2610 | this.yorigin, this.xorigin + this.xsize, this.yorigin - this.ysize, this.GRC.CANVAS); | ||
| 2611 | this.gfx.add_point(this.xorigin, this.yorigin - this.ysize); | ||
| 2612 | this.gfx.close_path(); | ||
| 2613 | |||
| 2614 | //this.ctx.rect(this.xorigin, this.yorigin - this.ysize - 1.0, this.xsize, this.ysize + 2.0); | ||
| 2615 | //this.ctx.clip(); | ||
| 2616 | |||
| 2617 | if (this.minval > 0.0) areazero = this.minval; | ||
| 2618 | if (this.maxval < 0.0) areazero = this.maxval; | ||
| 2619 | |||
| 2620 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 2621 | switch (this.gdes[i].gf) { | ||
| 2622 | case RrdGraphDesc.GF_CDEF: | ||
| 2623 | case RrdGraphDesc.GF_VDEF: | ||
| 2624 | case RrdGraphDesc.GF_DEF: | ||
| 2625 | case RrdGraphDesc.GF_PRINT: | ||
| 2626 | case RrdGraphDesc.GF_GPRINT: | ||
| 2627 | case RrdGraphDesc.GF_COMMENT: | ||
| 2628 | case RrdGraphDesc.GF_TEXTALIGN: | ||
| 2629 | case RrdGraphDesc.GF_HRULE: | ||
| 2630 | case RrdGraphDesc.GF_VRULE: | ||
| 2631 | case RrdGraphDesc.GF_XPORT: | ||
| 2632 | case RrdGraphDesc.GF_SHIFT: | ||
| 2633 | break; | ||
| 2634 | case RrdGraphDesc.GF_TICK: | ||
| 2635 | for (var ii = 0; ii < this.xsize; ii++) { | ||
| 2636 | if (!isNaN(this.gdes[i].p_data[ii]) && this.gdes[i].p_data[ii] != 0.0) { | ||
| 2637 | if (this.gdes[i].yrule > 0) { | ||
| 2638 | this.gfx.line(this.xorigin + ii, this.yorigin + 1.0, | ||
| 2639 | this.xorigin + ii, this.yorigin - this.gdes[i].yrule * this.ysize, 1.0, this.gdes[i].col); | ||
| 2640 | } else if (this.gdes[i].yrule < 0) { | ||
| 2641 | this.gfx.line(this.xorigin + ii, this.yorigin - this.ysize - 1.0, | ||
| 2642 | this.xorigin + ii, this.yorigin - this.ysize - this.gdes[i].yrule * this.ysize, 1.0, this.gdes[i].col); | ||
| 2643 | } | ||
| 2644 | } | ||
| 2645 | } | ||
| 2646 | break; | ||
| 2647 | case RrdGraphDesc.GF_LINE: | ||
| 2648 | case RrdGraphDesc.GF_AREA: | ||
| 2649 | var diffval = this.maxval - this.minval; | ||
| 2650 | var maxlimit = this.maxval + 9 * diffval; | ||
| 2651 | var minlimit = this.minval - 9 * diffval; | ||
| 2652 | for (var ii = 0; ii < this.xsize; ii++) { | ||
| 2653 | if (!isNaN(this.gdes[i].p_data[ii])) { // FIXME NaN < ??? | ||
| 2654 | if (!isFinite(this.gdes[i].p_data[ii])) { | ||
| 2655 | if (this.gdes[i].p_data[ii] > 0) this.gdes[i].p_data[ii] = this.maxval; | ||
| 2656 | else this.gdes[i].p_data[ii] = this.minval; | ||
| 2657 | } | ||
| 2658 | if (this.gdes[i].p_data[ii] > maxlimit) this.gdes[i].p_data[ii] = maxlimit; | ||
| 2659 | if (this.gdes[i].p_data[ii] < minlimit) this.gdes[i].p_data[ii] = minlimit; | ||
| 2660 | } | ||
| 2661 | } | ||
| 2662 | var color = this.parse_color(this.gdes[i].col); // if (this.gdes[i].col.alpha != 0.0) | ||
| 2663 | if (color[3] != 0.0) { | ||
| 2664 | if (this.gdes[i].gf === RrdGraphDesc.GF_LINE) { | ||
| 2665 | var last_y = 0.0; | ||
| 2666 | var draw_on = false; | ||
| 2667 | |||
| 2668 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
| 2669 | this.gfx.stroke_begin(this.gdes[i].linewidth, this.gdes[i].col); | ||
| 2670 | for (var ii = 1; ii < this.xsize; ii++) { | ||
| 2671 | if (isNaN(this.gdes[i].p_data[ii]) || (this.slopemode && isNaN(this.gdes[i].p_data[ii - 1]))) { | ||
| 2672 | draw_on = false; | ||
| 2673 | continue; | ||
| 2674 | } | ||
| 2675 | if (!draw_on) { | ||
| 2676 | last_y = this.ytr(this.gdes[i].p_data[ii]); | ||
| 2677 | if (!this.slopemode) { | ||
| 2678 | var x = ii - 1 + this.xorigin; | ||
| 2679 | var y = last_y; | ||
| 2680 | this.gfx.moveTo(x, y); | ||
| 2681 | x = ii + this.xorigin; | ||
| 2682 | y = last_y; | ||
| 2683 | this.gfx.lineTo(x, y) | ||
| 2684 | } else { | ||
| 2685 | var x = ii - 1 + this.xorigin; | ||
| 2686 | var y = this.ytr(this.gdes[i].p_data[ii - 1]); | ||
| 2687 | this.gfx.moveTo(x, y); | ||
| 2688 | x = ii + this.xorigin; | ||
| 2689 | y = last_y; | ||
| 2690 | this.gfx.lineTo(x, y); | ||
| 2691 | } | ||
| 2692 | draw_on = true; | ||
| 2693 | } else { | ||
| 2694 | var x1 = ii + this.xorigin; | ||
| 2695 | var y1 = this.ytr(this.gdes[i].p_data[ii]); | ||
| 2696 | |||
| 2697 | if (!this.slopemode && !this.AlmostEqual2sComplement(y1, last_y, 4)) { | ||
| 2698 | var x = ii - 1 + this.xorigin; | ||
| 2699 | var y = y1; | ||
| 2700 | |||
| 2701 | this.gfx.lineTo(x, y); | ||
| 2702 | } | ||
| 2703 | last_y = y1; | ||
| 2704 | this.gfx.lineTo(x1, y1); | ||
| 2705 | } | ||
| 2706 | } | ||
| 2707 | this.gfx.stroke_end(); | ||
| 2708 | } else { | ||
| 2709 | var idxI = -1; | ||
| 2710 | var foreY = []; | ||
| 2711 | var foreX = []; | ||
| 2712 | var backY = []; | ||
| 2713 | var backX = []; | ||
| 2714 | var drawem = false; | ||
| 2715 | |||
| 2716 | for (ii = 0; ii <= this.xsize; ii++) { | ||
| 2717 | var ybase, ytop; | ||
| 2718 | |||
| 2719 | if (idxI > 0 && (drawem || ii === this.xsize)) { | ||
| 2720 | var cntI = 1; | ||
| 2721 | var lastI = 0; | ||
| 2722 | |||
| 2723 | while (cntI < idxI && this.AlmostEqual2sComplement(foreY [lastI], foreY[cntI], 4) && | ||
| 2724 | this.AlmostEqual2sComplement(foreY [lastI], foreY [cntI + 1], 4)) cntI++; | ||
| 2725 | this.gfx.new_area(backX[0], backY[0], foreX[0], foreY[0], foreX[cntI], foreY[cntI], this.gdes[i].col); | ||
| 2726 | while (cntI < idxI) { | ||
| 2727 | lastI = cntI; | ||
| 2728 | cntI++; | ||
| 2729 | while (cntI < idxI && | ||
| 2730 | this.AlmostEqual2sComplement(foreY [lastI], foreY[cntI], 4) && | ||
| 2731 | this.AlmostEqual2sComplement(foreY [lastI], foreY [cntI + 1], 4)) cntI++; | ||
| 2732 | this.gfx.add_point(foreX[cntI], foreY[cntI]); | ||
| 2733 | } | ||
| 2734 | this.gfx.add_point(backX[idxI], backY[idxI]); | ||
| 2735 | while (idxI > 1) { | ||
| 2736 | lastI = idxI; | ||
| 2737 | idxI--; | ||
| 2738 | while (idxI > 1 && | ||
| 2739 | this.AlmostEqual2sComplement(backY [lastI], backY[idxI], 4) && | ||
| 2740 | this.AlmostEqual2sComplement(backY [lastI], backY [idxI - 1], 4)) idxI--; | ||
| 2741 | this.gfx.add_point(backX[idxI], backY[idxI]); | ||
| 2742 | } | ||
| 2743 | idxI = -1; | ||
| 2744 | drawem = false; | ||
| 2745 | this.gfx.close_path(); | ||
| 2746 | } | ||
| 2747 | if (drawem) { | ||
| 2748 | drawem = false; | ||
| 2749 | idxI = -1; | ||
| 2750 | } | ||
| 2751 | if (ii === this.xsize) | ||
| 2752 | break; | ||
| 2753 | if (!this.slopemode && ii === 0) | ||
| 2754 | continue; | ||
| 2755 | if (isNaN(this.gdes[i].p_data[ii])) { | ||
| 2756 | drawem = true; | ||
| 2757 | continue; | ||
| 2758 | } | ||
| 2759 | ytop = this.ytr(this.gdes[i].p_data[ii]); | ||
| 2760 | if (lastgdes && this.gdes[i].stack) ybase = this.ytr(lastgdes.p_data[ii]); | ||
| 2761 | else ybase = this.ytr(areazero); | ||
| 2762 | if (ybase === ytop) { | ||
| 2763 | drawem = true; | ||
| 2764 | continue; | ||
| 2765 | } | ||
| 2766 | if (ybase > ytop) { | ||
| 2767 | var extra = ytop; | ||
| 2768 | ytop = ybase; | ||
| 2769 | ybase = extra; | ||
| 2770 | } | ||
| 2771 | if (!this.slopemode) { | ||
| 2772 | backY[++idxI] = ybase - 0.2; | ||
| 2773 | backX[idxI] = ii + this.xorigin - 1; | ||
| 2774 | foreY[idxI] = ytop + 0.2; | ||
| 2775 | foreX[idxI] = ii + this.xorigin - 1; | ||
| 2776 | } | ||
| 2777 | backY[++idxI] = ybase - 0.2; | ||
| 2778 | backX[idxI] = ii + this.xorigin; | ||
| 2779 | foreY[idxI] = ytop + 0.2; | ||
| 2780 | foreX[idxI] = ii + this.xorigin; | ||
| 2781 | } | ||
| 2782 | } | ||
| 2783 | } | ||
| 2784 | /* if color != 0x0 */ | ||
| 2785 | /* make sure we do not run into trouble when stacking on NaN */ | ||
| 2786 | for (ii = 0; ii < this.xsize; ii++) { | ||
| 2787 | if (isNaN(this.gdes[i].p_data[ii])) { | ||
| 2788 | if (lastgdes && (this.gdes[i].stack)) this.gdes[i].p_data[ii] = lastgdes.p_data[ii]; | ||
| 2789 | else this.gdes[i].p_data[ii] = areazero; | ||
| 2790 | } | ||
| 2791 | } | ||
| 2792 | lastgdes = this.gdes[i]; //lastgdes = &(this.gdes[i]); | ||
| 2793 | break; | ||
| 2794 | case RrdGraphDesc.GF_STACK: | ||
| 2795 | throw new RrdGraphError("STACK should already be turned into LINE or AREA here"); | ||
| 2796 | break; | ||
| 2797 | } | ||
| 2798 | } | ||
| 2799 | //cairo_reset_clip(this.cr); | ||
| 2800 | if (!this.only_graph) | ||
| 2801 | this.grid_paint(); | ||
| 2802 | if (!this.only_graph) | ||
| 2803 | this.axis_paint(); | ||
| 2804 | /* the RULES are the last thing to paint ... */ | ||
| 2805 | for (var i = 0, gdes_c = this.gdes.length; i < gdes_c; i++) { | ||
| 2806 | switch (this.gdes[i].gf) { | ||
| 2807 | case RrdGraphDesc.GF_HRULE: | ||
| 2808 | if (this.gdes[i].yrule >= this.minval && this.gdes[i].yrule <= this.maxval) { | ||
| 2809 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
| 2810 | this.gfx.line(this.xorigin, this.ytr(this.gdes[i].yrule), | ||
| 2811 | this.xorigin + this.xsize, this.ytr(this.gdes[i].yrule), 1.0, this.gdes[i].col); | ||
| 2812 | } | ||
| 2813 | break; | ||
| 2814 | case RrdGraphDesc.GF_VRULE: | ||
| 2815 | if (this.gdes[i].xrule >= this.start && this.gdes[i].xrule <= this.end) { | ||
| 2816 | if (this.gdes[i].dash) this.gfx.set_dash(this.gdes[i].p_dashes, this.gdes[i].ndash, this.gdes[i].offset); | ||
| 2817 | this.gfx.line(this.xtr(this.gdes[i].xrule), this.yorigin, | ||
| 2818 | this.xtr(this.gdes[i].xrule), this.yorigin - this.ysize, 1.0, this.gdes[i].col); | ||
| 2819 | } | ||
| 2820 | break; | ||
| 2821 | default: | ||
| 2822 | break; | ||
| 2823 | } | ||
| 2824 | } | ||
| 2825 | return 0; | ||
| 2826 | }; | ||
| 2827 | |||
| 2828 | RrdGraph.prototype.find_var = function(key) | ||
| 2829 | { | ||
| 2830 | for (var ii = 0, gdes_c = this.gdes.length; ii < gdes_c; ii++) { | ||
| 2831 | if ((this.gdes[ii].gf === RrdGraphDesc.GF_DEF || | ||
| 2832 | this.gdes[ii].gf === RrdGraphDesc.GF_VDEF || | ||
| 2833 | this.gdes[ii].gf === RrdGraphDesc.GF_CDEF) | ||
| 2834 | && this.gdes[ii].vname === key) { | ||
| 2835 | return ii; | ||
| 2836 | } | ||
| 2837 | } | ||
| 2838 | return -1; | ||
| 2839 | }; | ||
| 2840 | |||
| 2841 | RrdGraph.prototype.gdes_add_def = function (vname, rrdfile, name, cf, step, start, end, reduce) | ||
| 2842 | { | ||
| 2843 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_DEF, vname, rrdfile, name, cf, step, start, end, reduce)); | ||
| 2844 | }; | ||
| 2845 | |||
| 2846 | RrdGraph.prototype.gdes_add_cdef = function (vname, rpn) | ||
| 2847 | { | ||
| 2848 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_CDEF, vname, rpn)); | ||
| 2849 | }; | ||
| 2850 | |||
| 2851 | RrdGraph.prototype.gdes_add_vdef = function (vname, rpn) | ||
| 2852 | { | ||
| 2853 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VDEF, vname, rpn)); | ||
| 2854 | }; | ||
| 2855 | |||
| 2856 | RrdGraph.prototype.gdes_add_shift = function (vname, offset) | ||
| 2857 | { | ||
| 2858 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_SHIFT, vname, offset)); | ||
| 2859 | }; | ||
| 2860 | |||
| 2861 | RrdGraph.prototype.gdes_add_line = function (width, value, color, legend, stack) | ||
| 2862 | { | ||
| 2863 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_LINE, width, value, color, legend, stack)); | ||
| 2864 | }; | ||
| 2865 | |||
| 2866 | RrdGraph.prototype.gdes_add_area = function (value, color, legend, stack) | ||
| 2867 | { | ||
| 2868 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_AREA, value, color, legend, stack)); | ||
| 2869 | }; | ||
| 2870 | |||
| 2871 | RrdGraph.prototype.gdes_add_tick = function (vname, color, fraction, legend) | ||
| 2872 | { | ||
| 2873 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_TICK, vname, color, fraction, legend)); | ||
| 2874 | }; | ||
| 2875 | |||
| 2876 | RrdGraph.prototype.gdes_add_gprint = function (vname, cf, format, strftimefmt) | ||
| 2877 | { | ||
| 2878 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_GPRINT, vname, cf, format, strftimefmt)); | ||
| 2879 | }; | ||
| 2880 | |||
| 2881 | RrdGraph.prototype.gdes_add_comment = function (text) | ||
| 2882 | { | ||
| 2883 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_COMMENT, text)); | ||
| 2884 | }; | ||
| 2885 | |||
| 2886 | RrdGraph.prototype.gdes_add_textalign = function (align) | ||
| 2887 | { | ||
| 2888 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_TEXTALING, align)); | ||
| 2889 | }; | ||
| 2890 | |||
| 2891 | RrdGraph.prototype.gdes_add_vrule = function (time, color, legend) | ||
| 2892 | { | ||
| 2893 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_VRULE, time, color, legend)); | ||
| 2894 | }; | ||
| 2895 | |||
| 2896 | RrdGraph.prototype.gdes_add_hrule = function (value, color, legend) | ||
| 2897 | { | ||
| 2898 | this.gdes.push(new RrdGraphDesc(this, RrdGraphDesc.GF_HRULE, value, color, legend)); | ||
| 2899 | }; | ||
| 2900 | |||
| 2901 | RrdGraph.prototype.tmt_conv = function (str) | ||
| 2902 | { | ||
| 2903 | switch (str) { | ||
| 2904 | case 'SECOND': return RrdGraph.TMT_SECOND; | ||
| 2905 | case 'MINUTE': return RrdGraph.TMT_MINUTE; | ||
| 2906 | case 'HOUR': return RrdGraph.TMT_HOUR; | ||
| 2907 | case 'DAY': return RrdGraph.TMT_DAY; | ||
| 2908 | case 'WEEK': return RrdGraph.TMT_WEEK; | ||
| 2909 | case 'MONTH': return RrdGraph.TMT_MONTH; | ||
| 2910 | case 'YEAR': return RrdGraph.TMT_YEAR; | ||
| 2911 | } | ||
| 2912 | return -1; | ||
| 2913 | }; | ||
| 2914 | |||
diff --git a/js/RrdJson.js b/js/RrdJson.js new file mode 100644 index 0000000..85b7f11 --- /dev/null +++ b/js/RrdJson.js | |||
| @@ -0,0 +1,588 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * | ||
| 18 | * Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 19 | **/ | ||
| 20 | |||
| 21 | "use strict"; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * RrdJson | ||
| 25 | * @constructor | ||
| 26 | */ | ||
| 27 | var RrdJson = function() { | ||
| 28 | if (arguments.length == 1) { | ||
| 29 | this.init1.apply(this, arguments); | ||
| 30 | } else if (arguments.length == 2) { | ||
| 31 | this.init2.apply(this, arguments); | ||
| 32 | } else if (arguments.length == 3) { | ||
| 33 | this.init3.apply(this, arguments); | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | RrdJson.prototype = { | ||
| 38 | graph: null, | ||
| 39 | json: null, | ||
| 40 | |||
| 41 | init1: function (rrdgraph) | ||
| 42 | { | ||
| 43 | this.graph = rrdgraph | ||
| 44 | }, | ||
| 45 | init2: function (rrdgraph, jsonstr) | ||
| 46 | { | ||
| 47 | this.json = JSON.parse(jsonstr); | ||
| 48 | this.graph = rrdgraph | ||
| 49 | }, | ||
| 50 | init3: function (gfx, fetch, jsonstr) | ||
| 51 | { | ||
| 52 | this.json = JSON.parse(jsonstr); | ||
| 53 | this.graph = new RrdGraph(gfx, fetch); | ||
| 54 | }, | ||
| 55 | parse: function() | ||
| 56 | { | ||
| 57 | for (var option in this.json) { | ||
| 58 | switch(option) { | ||
| 59 | case 'alt_autoscale': | ||
| 60 | this.graph.alt_autoscale = this.json.alt_autoscale; | ||
| 61 | break; | ||
| 62 | case 'base': | ||
| 63 | this.graph.base = parseInt(this.json.base, 10); | ||
| 64 | if (this.graph.base !== 1000 && this.graph.base !== 1024) | ||
| 65 | throw 'the only sensible value for base apart from 1000 is 1024'; | ||
| 66 | break; | ||
| 67 | case 'color': | ||
| 68 | for (var color in this.json.color) { | ||
| 69 | if (color in this.graph.GRC) { | ||
| 70 | this.graph.GRC[color] = this.json.color[color]; | ||
| 71 | } else { | ||
| 72 | throw "invalid color name '"+name+"'"; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | break; | ||
| 76 | case 'full_size_mode': | ||
| 77 | this.graph.full_size_mode = this.json.full_size_mode; | ||
| 78 | break; | ||
| 79 | case 'slope_mode': | ||
| 80 | this.graph.slopemode = this.json.slope_mode; | ||
| 81 | break; | ||
| 82 | case 'end': | ||
| 83 | this.graph.end_t = new RrdTime(this.json.end); | ||
| 84 | break; | ||
| 85 | case 'force_rules_legend': | ||
| 86 | this.graph.force_rules_legend = this.json.force_rules_legend; | ||
| 87 | break; | ||
| 88 | case 'no_legend': | ||
| 89 | this.graph.no_legend = this.json.no_legend; | ||
| 90 | break; | ||
| 91 | case 'height': | ||
| 92 | this.graph.ysize = this.json.height; | ||
| 93 | break; | ||
| 94 | case 'no_minor': | ||
| 95 | this.graph.no_minor = this.json.no_minor; | ||
| 96 | break; | ||
| 97 | case 'alt_autoscale_min': | ||
| 98 | this.graph.alt_autoscale_min = this.json.alt_autoscale_min; | ||
| 99 | break; | ||
| 100 | case 'only_graph': | ||
| 101 | this.graph.only_graph = this.json.only_graph; | ||
| 102 | break; | ||
| 103 | case 'units_length': | ||
| 104 | this.graph.unitslength = this.json.units_length; // FIXME | ||
| 105 | this.graph.forceleftspace = true; | ||
| 106 | break; | ||
| 107 | case 'lower_limit': | ||
| 108 | if (this.json.lower_limit === null) this.graph.setminval = Number.NaN; | ||
| 109 | else this.graph.setminval = this.json.lower_limit; | ||
| 110 | break; | ||
| 111 | case 'alt_autoscale_max': | ||
| 112 | this.graph.alt_autoscale_max = this.json.alt_autoscale_max; | ||
| 113 | break; | ||
| 114 | case 'zoom': | ||
| 115 | this.graph.zoom = this.json.zoom; | ||
| 116 | if (this.graph.zoom <= 0.0) | ||
| 117 | throw "zoom factor must be > 0"; | ||
| 118 | break; | ||
| 119 | case 'no_gridfit': | ||
| 120 | this.graph.gridfit = this.json.no_gridfit; | ||
| 121 | break; | ||
| 122 | case 'font': | ||
| 123 | for (var font in this.json.font) { | ||
| 124 | if (font in this.graph.TEXT) { | ||
| 125 | if (this.json.font[font].size !== undefined) | ||
| 126 | this.graph.TEXT[font].size = this.json.font[font].size; | ||
| 127 | if (this.json.font[font].font !== undefined) | ||
| 128 | this.graph.TEXT[font].font = this.json.font[font].font; | ||
| 129 | } else { | ||
| 130 | throw "invalid text property name"; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | break; | ||
| 134 | case 'logarithmic': | ||
| 135 | this.graph.logarithmic = this.json.logarithmic; | ||
| 136 | break; | ||
| 137 | case 'rigid': | ||
| 138 | this.graph.rigid = this.json.rigid; | ||
| 139 | break; | ||
| 140 | case 'step': | ||
| 141 | this.graph.step = this.json.step; | ||
| 142 | break; | ||
| 143 | case 'start': | ||
| 144 | this.graph.start_t = new RrdTime(this.json.start); | ||
| 145 | break; | ||
| 146 | case 'tabwidth': | ||
| 147 | this.graph.tabwidth = this.json.tabwidth; | ||
| 148 | break; | ||
| 149 | case 'title': | ||
| 150 | this.graph.title = this.json.title; | ||
| 151 | break; | ||
| 152 | case 'upper_limit': | ||
| 153 | if (this.json.upper_limit === null) this.graph.setmaxval = Number.NaN; | ||
| 154 | else this.graph.setmaxval = this.json.upper_limit; | ||
| 155 | break; | ||
| 156 | case 'vertical_label': | ||
| 157 | this.graph.ylegend = this.json.vertical_label; | ||
| 158 | break; | ||
| 159 | case 'watermark': | ||
| 160 | this.graph.watermark = this.json.watermark; | ||
| 161 | break; | ||
| 162 | case 'width': | ||
| 163 | this.graph.xsize = this.json.width; | ||
| 164 | if (this.graph.xsize < 10) | ||
| 165 | throw "width below 10 pixels"; | ||
| 166 | break; | ||
| 167 | case 'units_exponent': | ||
| 168 | this.graph.unitsexponent = this.json.units_exponent; | ||
| 169 | break; | ||
| 170 | case 'x_grid': | ||
| 171 | break; | ||
| 172 | case 'alt_ygrid': | ||
| 173 | this.graph.alt_ygrid = this.json.alt_ygrid; | ||
| 174 | break; | ||
| 175 | case 'y_grid': | ||
| 176 | break; | ||
| 177 | case 'lazy': | ||
| 178 | this.graph.lazy = this.json.lazy; | ||
| 179 | break; | ||
| 180 | case 'units': | ||
| 181 | break; | ||
| 182 | case 'disable_rrdtool_tag': | ||
| 183 | this.graph.no_rrdtool_tag = this.json.disable_rrdtool_tag; | ||
| 184 | break; | ||
| 185 | case 'right_axis': | ||
| 186 | break; | ||
| 187 | case 'right_axis_label': | ||
| 188 | this.graph.second_axis_legend = this.json.right_axis_label; | ||
| 189 | break; | ||
| 190 | case 'right_axis_format': | ||
| 191 | this.graph.second_axis_format = this.json.right_axis_format; | ||
| 192 | break; | ||
| 193 | case 'legend_position': | ||
| 194 | if (this.json.legend_position === "north") { | ||
| 195 | this.graph.legendposition = this.graph.LEGEND_POS.NORTH; | ||
| 196 | } else if (this.json.legend_position === "west") { | ||
| 197 | this.graph.legendposition = this.graph.LEGEND_POS.WEST; | ||
| 198 | } else if (this.json.legend_position === "south") { | ||
| 199 | this.graph.legendposition = this.graph.LEGEND_POS.SOUTH; | ||
| 200 | } else if (this.json.legend_position === "east") { | ||
| 201 | this.graph.legendposition = this.graph.LEGEND_POS.EAST; | ||
| 202 | } else { | ||
| 203 | throw "unknown legend-position '"+value+"'"; | ||
| 204 | } | ||
| 205 | break; | ||
| 206 | case 'legend_direction': | ||
| 207 | if (this.json.legend_direction === "topdown") { | ||
| 208 | this.graph.legenddirection = this.graph.LEGEND_DIR.TOP_DOWN; | ||
| 209 | } else if (this.json.legend_direction === "bottomup") { | ||
| 210 | this.graph.legenddirection = this.graph.LEGEND_DIR.BOTTOM_UP; | ||
| 211 | } else { | ||
| 212 | throw "unknown legend-position '"+value+"'"; | ||
| 213 | } | ||
| 214 | break; | ||
| 215 | case 'border': | ||
| 216 | this.graph.draw_3d_border = this.json.border; | ||
| 217 | break; | ||
| 218 | case 'grid_dash': | ||
| 219 | if (this.json.grid_dash.length !== 2) | ||
| 220 | throw "expected grid-dash format float:float"; | ||
| 221 | this.graph.grid_dash_on = this.json.grid_dash[0]; | ||
| 222 | this.graph.grid_dash_off = this.json.grid_dash[1]; | ||
| 223 | break; | ||
| 224 | case 'dynamic_labels': | ||
| 225 | this.graph.dynamic_labels = this.json.dynamic_labels; | ||
| 226 | break; | ||
| 227 | case 'gdes': | ||
| 228 | this.parse_gdes(this.json.gdes); | ||
| 229 | break; | ||
| 230 | default: | ||
| 231 | throw 'Unknow option "'+option+'"'; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | var start_end = RrdTime.proc_start_end(this.graph.start_t, this.graph.end_t); // FIXME here? | ||
| 235 | this.graph.start = start_end[0]; | ||
| 236 | this.graph.end = start_end[1]; | ||
| 237 | }, | ||
| 238 | parse_gdes: function (gdes) | ||
| 239 | { | ||
| 240 | for (var i = 0, gdes_c = gdes.length; i < gdes_c; i++) { | ||
| 241 | switch (gdes[i].type) { | ||
| 242 | // GPRINT:vname:format | ||
| 243 | case 'GPRINT': | ||
| 244 | this.graph.gdes_add_gprint(gdes[i].vname, gdes[i].cf, gdes[i].format, gdes[i].strftm); | ||
| 245 | break; | ||
| 246 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 247 | case 'LINE': | ||
| 248 | this.graph.gdes_add_line(gdes[i].width, gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack); | ||
| 249 | break; | ||
| 250 | // AREA:value[#color][:[legend][:STACK]] | ||
| 251 | case 'AREA': | ||
| 252 | this.graph.gdes_add_area(gdes[i].value, gdes[i].color, gdes[i].legend, gdes[i].stack); | ||
| 253 | break; | ||
| 254 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
| 255 | case 'TICK': | ||
| 256 | this.graph.gdes_add_tick(gdes[i].vname, gdes[i].color, gdes[i].fraction, gdes[i].legend); | ||
| 257 | break; | ||
| 258 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 259 | case 'HRULE': | ||
| 260 | this.graph.gdes_add_hrule(gdes[i].value, gdes[i].color, gdes[i].legend); | ||
| 261 | break; | ||
| 262 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 263 | case 'VRULE': | ||
| 264 | this.graph.gdes_add_vrule(gdes[i].time, gdes[i].color, gdes[i].legend); | ||
| 265 | break; | ||
| 266 | // COMMENT:text | ||
| 267 | case 'COMMENT': | ||
| 268 | this.graph.gdes_add_comment(gdes[i].legend); | ||
| 269 | break; | ||
| 270 | // TEXTALIGN:{left|right|justified|center} | ||
| 271 | case 'TEXTALIGN': | ||
| 272 | switch (gdes[i].align) { | ||
| 273 | case 'left': | ||
| 274 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_LEFT); | ||
| 275 | break | ||
| 276 | case 'right': | ||
| 277 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_RIGHT); | ||
| 278 | break | ||
| 279 | case 'justified': | ||
| 280 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_JUSTIFIED); | ||
| 281 | break | ||
| 282 | case 'center': | ||
| 283 | this.graph.gdes_add_textaling(RrdGraphDesc.TXA_CENTER); | ||
| 284 | break | ||
| 285 | } | ||
| 286 | break; | ||
| 287 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
| 288 | case 'DEF': | ||
| 289 | this.graph.gdes_add_def(gdes[i].vname, gdes[i].rrdfile, gdes[i].name, gdes[i].cf, gdes[i].step, gdes[i].start, gdes[i].end, gdes[i].reduce) | ||
| 290 | break; | ||
| 291 | // CDEF:vname=RPN expression | ||
| 292 | case 'CDEF': | ||
| 293 | this.graph.gdes_add_cdef(gdes[i].vname, gdes[i].rpn); | ||
| 294 | break; | ||
| 295 | // VDEF:vname=RPN expression | ||
| 296 | case 'VDEF': | ||
| 297 | this.graph.gdes_add_vdef(gdes[i].vname, gdes[i].rpn); | ||
| 298 | break; | ||
| 299 | // SHIFT:vname:offset | ||
| 300 | case 'SHIFT': | ||
| 301 | this.graph.gdes_add_shift(gdes[i].vname, gdes[i].offset); | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | }, | ||
| 306 | dump: function(full) | ||
| 307 | { | ||
| 308 | this.json = {}; | ||
| 309 | |||
| 310 | if (full === undefined) full = false; | ||
| 311 | |||
| 312 | if (this.graph.alt_autoscale != false || full) | ||
| 313 | this.json.alt_autoscale = this.graph.alt_autoscale; | ||
| 314 | |||
| 315 | if (this.graph.base != 1000 || full) | ||
| 316 | this.json.base = this.graph.base; | ||
| 317 | |||
| 318 | this.json.color = {}; | ||
| 319 | |||
| 320 | if (this.graph.GRC.CANVAS != 'rgba(255, 255, 255, 1.0)' || full) | ||
| 321 | this.json.color.CANVAS = this.graph.GRC.CANVAS; | ||
| 322 | if (this.graph.GRC.BACK != 'rgba(242,242, 242, 1.0)' || full) | ||
| 323 | this.json.color.BACK = this.graph.GRC.BACK; | ||
| 324 | if (this.graph.GRC.SHADEA != 'rgba(207, 207, 207, 1.0)' || full) | ||
| 325 | this.json.color.SHADEA = this.graph.GRC.SHADEA; | ||
| 326 | if (this.graph.GRC.SHADEB != 'rgba(158, 158, 158, 1.0)' || full) | ||
| 327 | this.json.color.SHADEB = this.graph.GRC.SHADEB; | ||
| 328 | if (this.graph.GRC.GRID != 'rgba(143, 143, 143, 0.75)' || full) | ||
| 329 | this.json.color.GRID = this.graph.GRC.GRID; | ||
| 330 | if (this.graph.GRC.MGRID != 'rgba(222, 79, 79, 0.60)' || full) | ||
| 331 | this.json.color.MGRID = this.graph.GRC.MGRID; | ||
| 332 | if (this.graph.GRC.FONT != 'rgba(0, 0, 0, 1.0)' || full) | ||
| 333 | this.json.color.FONT = this.graph.GRC.FONT; | ||
| 334 | if (this.graph.GRC.ARROW != 'rgba(127, 31, 31, 1.0)' || full) | ||
| 335 | this.json.color.ARROW = this.graph.GRC.ARROW; | ||
| 336 | if (this.graph.GRC.AXIS != 'rgba(31, 31, 31, 1.0)' || full) | ||
| 337 | this.json.color.AXIS = this.graph.GRC.AXIS; | ||
| 338 | if (this.graph.GRC.FRAME != 'rgba(0, 0, 0, 1.0)' || full) | ||
| 339 | this.json.color.FRAME = this.graph.GRC.FRAME; | ||
| 340 | |||
| 341 | if (Object.keys(this.json.color) == 0) delete this.json.color; | ||
| 342 | |||
| 343 | if (this.graph.full_size_mode != false || full) | ||
| 344 | this.json.full_size_mode = this.graph.full_size_mode; | ||
| 345 | |||
| 346 | if (this.graph.slopemode != false || full) | ||
| 347 | this.json.slope_mode = this.graph.slopemode; | ||
| 348 | |||
| 349 | this.json.end = this.graph.end_t.tspec; | ||
| 350 | this.json.start = this.graph.start_t.tspec; | ||
| 351 | |||
| 352 | if (this.graph.force_rules_legend != false || full) | ||
| 353 | this.json.force_rules_legend = this.graph.force_rules_legend; | ||
| 354 | |||
| 355 | if (this.graph.no_legend != false || full) | ||
| 356 | this.json.no_legend = this.graph.no_legend; | ||
| 357 | |||
| 358 | this.json.width = this.graph.xsize; | ||
| 359 | this.json.height = this.graph.ysize; | ||
| 360 | |||
| 361 | if (this.graph.no_minor != false || full) | ||
| 362 | this.json.no_minor = this.graph.no_minor; | ||
| 363 | |||
| 364 | if (this.graph.alt_autoscale_min != false || full) | ||
| 365 | this.json.alt_autoscale_min = this.graph.alt_autoscale_min; | ||
| 366 | |||
| 367 | if (this.graph.only_graph != false || full) | ||
| 368 | this.json.only_graph = this.graph.only_graph; | ||
| 369 | |||
| 370 | if (this.graph.unitslength != 6 || full) | ||
| 371 | this.json.units_length = this.graph.unitslength; | ||
| 372 | |||
| 373 | if (!isNaN(this.graph.setminval) || full) | ||
| 374 | this.json.lower_limit = this.graph.setminval; | ||
| 375 | |||
| 376 | if (this.graph.alt_autoscale_max != false || full) | ||
| 377 | this.json.alt_autoscale_max = this.graph.alt_autoscale_max; | ||
| 378 | |||
| 379 | if (this.graph.zoom != 1 || full) | ||
| 380 | this.json.zoom = this.graph.zoom; | ||
| 381 | |||
| 382 | if (this.graph.gridfit != true || full) | ||
| 383 | this.json.no_gridfit = this.graph.gridfit; | ||
| 384 | |||
| 385 | this.json.font = {}; | ||
| 386 | if (this.graph.TEXT.DEFAULT.size != 11 || this.graph.TEXT.LEGEND.font != this.graph.DEFAULT_FONT || full) | ||
| 387 | this.json.font.DEFAULT = { size: this.graph.TEXT.DEFAULT.size, font: this.graph.TEXT.DEFAULT.font}; | ||
| 388 | if (this.graph.TEXT.TITLE.size != 12 || this.graph.TEXT.TITLE.font != this.graph.DEFAULT_FONT || full) | ||
| 389 | this.json.font.TITLE = { size: this.graph.TEXT.TITLE.size, font: this.graph.TEXT.TITLE.font}; | ||
| 390 | if (this.graph.TEXT.AXIS.size != 10 || this.graph.TEXT.AXIS.font != this.graph.DEFAULT_FONT || full) | ||
| 391 | this.json.font.AXIS = { size: this.graph.TEXT.AXIS.size, font: this.graph.TEXT.AXIS.font}; | ||
| 392 | if (this.graph.TEXT.UNIT.size != 11 || this.graph.TEXT.UNIT.font != this.graph.DEFAULT_FONT || full) | ||
| 393 | this.json.font.UNIT = { size: this.graph.TEXT.UNIT.size, font: this.graph.TEXT.UNIT.font}; | ||
| 394 | if (this.graph.TEXT.LEGEND.size != 11 || this.graph.TEXT.LEGEND.font != this.graph.DEFAULT_FONT || full) | ||
| 395 | this.json.font.LEGEND = { size: this.graph.TEXT.LEGEND.size, font: this.graph.TEXT.LEGEND.font}; | ||
| 396 | if (this.graph.TEXT.WATERMARK.size != 8 || this.graph.TEXT.WATERMARK.font != this.graph.DEFAULT_FONT || full) | ||
| 397 | this.json.font.WATERMARK = { size: this.graph.TEXT.WATERMARK.size, font: this.graph.TEXT.WATERMARK.font}; | ||
| 398 | |||
| 399 | if (Object.keys(this.json.font) == 0) delete this.json.font; | ||
| 400 | |||
| 401 | if (this.graph.logarithmic != false || full) | ||
| 402 | this.json.logarithmic = this.graph.logarithmic; | ||
| 403 | |||
| 404 | if (this.graph.rigid != false || full) | ||
| 405 | this.json.rigid = this.graph.rigid; | ||
| 406 | |||
| 407 | // this.json.step = this.graph.step; // FIXME | ||
| 408 | |||
| 409 | if (this.graph.tabwidth != 40 || full) | ||
| 410 | this.json.tabwidth = this.graph.tabwidth; | ||
| 411 | |||
| 412 | if (this.graph.title != '' || full) | ||
| 413 | this.json.title = this.graph.title; | ||
| 414 | |||
| 415 | if (!isNaN(this.graph.setmaxval) || full) | ||
| 416 | this.json.upper_limit = this.graph.setmaxval; | ||
| 417 | |||
| 418 | if (this.graph.ylegend != null || full) | ||
| 419 | this.json.vertical_label = this.graph.ylegend; | ||
| 420 | |||
| 421 | if (this.graph.watermark != null || full) | ||
| 422 | this.json.watermark = this.graph.watermark; | ||
| 423 | |||
| 424 | if (this.graph.unitsexponent != 9999 || full) | ||
| 425 | this.json.units_exponent = this.graph.unitsexponent; | ||
| 426 | |||
| 427 | // this.json.x-grid = // FIXME | ||
| 428 | |||
| 429 | if (this.graph.alt_ygrid != false || full) | ||
| 430 | this.json.alt_ygrid = this.graph.alt_ygrid; | ||
| 431 | |||
| 432 | // this.json.y_grid = // FIXME | ||
| 433 | |||
| 434 | // this.json.lazy = this.graph.lazy; | ||
| 435 | |||
| 436 | if (this.graph.force_units_si != false || full) | ||
| 437 | this.json.units = 'si'; // FIXME | ||
| 438 | |||
| 439 | if (this.graph.no_rrdtool_tag != false || full) | ||
| 440 | this.json.disable_rrdtool_tag = this.graph.no_rrdtool_tag; | ||
| 441 | |||
| 442 | // this.json.right_axis = FIXME | ||
| 443 | |||
| 444 | if (this.graph.second_axis_legend != null || full) | ||
| 445 | this.json.right_axis_label = this.graph.second_axis_legend; | ||
| 446 | if (this.graph.second_axis_format != null || full) | ||
| 447 | this.json.right_axis_format = this.graph.second_axis_format; | ||
| 448 | |||
| 449 | // this.json.legendposition = this.graph.legendposition; // FIXME | ||
| 450 | // this.json.legend-direction = this.graph.legenddirection; // FIXME | ||
| 451 | |||
| 452 | if (this.graph.draw_3d_border != 2 || full) | ||
| 453 | this.json.border = this.graph.draw_3d_border; | ||
| 454 | |||
| 455 | if (this.graph.grid_dash_on != 1 || this.graph.grid_dash_off != 1 || full) | ||
| 456 | this.json.grid_dash = [this.graph.grid_dash_on, this.graph.grid_dash_off] | ||
| 457 | |||
| 458 | if (this.graph.dynamic_labels != false || full) | ||
| 459 | this.json.dynamic_labels = this.graph.dynamic_labels; | ||
| 460 | |||
| 461 | this.json.gdes = []; | ||
| 462 | for (var i = 0, gdes_c = this.graph.gdes.length; i < gdes_c; i++) { | ||
| 463 | switch (this.graph.gdes[i].gf) { | ||
| 464 | // GPRINT:vname:format | ||
| 465 | case RrdGraphDesc.GF_GPRINT: | ||
| 466 | this.json.gdes.push({ | ||
| 467 | type: 'GPRINT', | ||
| 468 | vname: this.graph.gdes[i].vname, | ||
| 469 | cf: RrdGraphDesc.cf2str(this.graph.gdes[i].cf), | ||
| 470 | format: this.graph.gdes[i].format, | ||
| 471 | strftm: (this.graph.gdes[i].strftm === false ? undefined : this.graph.gdes[i].strftm) }); | ||
| 472 | break; | ||
| 473 | // LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 474 | case RrdGraphDesc.GF_LINE: | ||
| 475 | this.json.gdes.push({ | ||
| 476 | type: 'LINE', | ||
| 477 | width: this.graph.gdes[i].linewidth, | ||
| 478 | value: this.graph.gdes[i].vname, | ||
| 479 | color: this.graph.gdes[i].col, | ||
| 480 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)), | ||
| 481 | stack: (this.graph.gdes[i].stack === false ? undefined : this.graph.gdes[i].stack) }); | ||
| 482 | break; | ||
| 483 | // AREA:value[#color][:[legend][:STACK]] | ||
| 484 | case RrdGraphDesc.GF_AREA: | ||
| 485 | this.json.gdes.push({ | ||
| 486 | type: 'AREA', | ||
| 487 | value: this.graph.gdes[i].vname, | ||
| 488 | color: this.graph.gdes[i].col, | ||
| 489 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)), | ||
| 490 | stack: (this.graph.gdes[i].stack === false ? undefined : this.graph.gdes[i].stack) }); | ||
| 491 | break; | ||
| 492 | // TICK:vname#rrggbb[aa][:fraction[:legend]] | ||
| 493 | case RrdGraphDesc.GF_TICK: | ||
| 494 | this.json.gdes.push({ | ||
| 495 | type: 'TICK', | ||
| 496 | vname: this.graph.gdes[i].vname, | ||
| 497 | color: this.graph.gdes[i].col, | ||
| 498 | fraction: this.graph.gdes[i].yrule, | ||
| 499 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
| 500 | break; | ||
| 501 | // HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 502 | case RrdGraphDesc.GF_HRULE: | ||
| 503 | this.json.gdes.push({ | ||
| 504 | type: 'HRULE', | ||
| 505 | value: this.graph.gdes[i].yrule, | ||
| 506 | color: this.graph.gdes[i].col, | ||
| 507 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
| 508 | break; | ||
| 509 | // VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] | ||
| 510 | case RrdGraphDesc.GF_VRULE: | ||
| 511 | this.json.gdes.push({ | ||
| 512 | type: 'VRULE', | ||
| 513 | time: this.graph.gdes[i].xrule, | ||
| 514 | color: this.graph.gdes[i].col, | ||
| 515 | legend: (this.graph.gdes[i].legend === '' ? undefined : this.graph.gdes[i].legend.substr(2)) }); | ||
| 516 | break; | ||
| 517 | // COMMENT:text | ||
| 518 | case RrdGraphDesc.GF_COMMENT: | ||
| 519 | this.json.gdes.push({ | ||
| 520 | type: 'COMMENT', | ||
| 521 | legend: this.graph.gdes[i].legend}); | ||
| 522 | break; | ||
| 523 | // TEXTALIGN:{left|right|justified|center} | ||
| 524 | case RrdGraphDesc.GF_TEXTALIGN: | ||
| 525 | var align = ''; | ||
| 526 | switch (this.graph.gdes[i].txtalign) { | ||
| 527 | case RrdGraphDesc.TXA_LEFT: | ||
| 528 | align = 'left'; | ||
| 529 | break | ||
| 530 | case RrdGraphDesc.TXA_RIGHT: | ||
| 531 | align = 'right'; | ||
| 532 | break | ||
| 533 | case RrdGraphDesc.TXA_JUSTIFIED: | ||
| 534 | align = 'justified'; | ||
| 535 | break | ||
| 536 | case RrdGraphDesc.TXA_CENTER: | ||
| 537 | align = 'center'; | ||
| 538 | break | ||
| 539 | } | ||
| 540 | |||
| 541 | this.json.gdes.push({ | ||
| 542 | type: 'TEXTALIGN', | ||
| 543 | align: align }); | ||
| 544 | break; | ||
| 545 | // DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>] | ||
| 546 | case RrdGraphDesc.GF_DEF: | ||
| 547 | this.json.gdes.push({ | ||
| 548 | type: 'DEF', | ||
| 549 | vname: this.graph.gdes[i].vname, | ||
| 550 | rrdfile: this.graph.gdes[i].rrd, | ||
| 551 | name: this.graph.gdes[i].ds_nam, | ||
| 552 | cf: RrdGraphDesc.cf2str(this.graph.gdes[i].cf), | ||
| 553 | // step: this.graph.gdes[i].step, | ||
| 554 | step: undefined, | ||
| 555 | start: undefined, | ||
| 556 | // start: this.graph.gdes[i].start, // FIXME | ||
| 557 | end: undefined, | ||
| 558 | // end: this.graph.gdes[i].end, // FIXME | ||
| 559 | // reduce: RrdGraphDesc.cf2str(this.graph.gdes[i].cf_reduce) | ||
| 560 | reduce: undefined | ||
| 561 | }); | ||
| 562 | |||
| 563 | break; | ||
| 564 | // CDEF:vname=RPN expression | ||
| 565 | case RrdGraphDesc.GF_CDEF: | ||
| 566 | this.json.gdes.push({ | ||
| 567 | type: 'CDEF', | ||
| 568 | vname: this.graph.gdes[i].vname, | ||
| 569 | rpn: this.graph.gdes[i].rpnp.rpnexpr}); | ||
| 570 | break; | ||
| 571 | // VDEF:vname=RPN expression | ||
| 572 | case RrdGraphDesc.GF_VDEF: | ||
| 573 | this.json.gdes.push({ | ||
| 574 | type: 'VDEF', | ||
| 575 | vname: this.graph.gdes[i].vname, | ||
| 576 | rpn: this.graph.gdes[this.graph.gdes[i].vidx].vname+','+this.graph.gdes[i].vf.expr}); | ||
| 577 | break; | ||
| 578 | // SHIFT:vname:offset | ||
| 579 | case RrdGraphDesc.GF_SHIFT: | ||
| 580 | this.json.gdes.push({ | ||
| 581 | type: 'VDEF', | ||
| 582 | vname: this.graph.gdes[i].vname, | ||
| 583 | offset: this.shidx }); | ||
| 584 | break; | ||
| 585 | } | ||
| 586 | } | ||
| 587 | } | ||
| 588 | }; | ||
diff --git a/js/RrdRpn.js b/js/RrdRpn.js new file mode 100644 index 0000000..cbb5e8f --- /dev/null +++ b/js/RrdRpn.js | |||
| @@ -0,0 +1,616 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010 | ||
| 18 | * | ||
| 19 | * Convert to javascript: Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 20 | **/ | ||
| 21 | |||
| 22 | "use strict"; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * RrdRpnError | ||
| 26 | * @constructor | ||
| 27 | */ | ||
| 28 | var RrdRpnError = function (message) | ||
| 29 | { | ||
| 30 | this.prototype = Error.prototype; | ||
| 31 | this.name = "RrdRpnError"; | ||
| 32 | this.message = (message) ? message : "RPN stack underflow"; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * RrdRpn | ||
| 37 | * @constructor | ||
| 38 | */ | ||
| 39 | var RrdRpn = function (str_expr, gdes) /* parser */ | ||
| 40 | { | ||
| 41 | var steps = -1; | ||
| 42 | var expr; | ||
| 43 | var exprs = str_expr.split(','); | ||
| 44 | |||
| 45 | this.rpnexpr = str_expr; | ||
| 46 | this.rpnp = []; | ||
| 47 | this.rpnstack = null; | ||
| 48 | |||
| 49 | for(var i=0, len=exprs.length; i < len; i++) { | ||
| 50 | expr=exprs[i].toUpperCase(); | ||
| 51 | |||
| 52 | steps++; | ||
| 53 | this.rpnp[steps] = {}; | ||
| 54 | |||
| 55 | if (!isNaN(expr)) { | ||
| 56 | this.rpnp[steps].op = RrdRpn.OP_NUMBER; | ||
| 57 | this.rpnp[steps].val = parseFloat(expr); | ||
| 58 | } | ||
| 59 | else if (expr === '+') this.rpnp[steps].op = RrdRpn.OP_ADD; | ||
| 60 | else if (expr === '-') this.rpnp[steps].op = RrdRpn.OP_SUB; | ||
| 61 | else if (expr === '*') this.rpnp[steps].op = RrdRpn.OP_MUL; | ||
| 62 | else if (expr === '/') this.rpnp[steps].op = RrdRpn.OP_DIV; | ||
| 63 | else if (expr === '%') this.rpnp[steps].op = RrdRpn.OP_MOD; | ||
| 64 | else if (expr === 'SIN') this.rpnp[steps].op = RrdRpn.OP_SIN; | ||
| 65 | else if (expr === 'COS') this.rpnp[steps].op = RrdRpn.OP_COS; | ||
| 66 | else if (expr === 'LOG') this.rpnp[steps].op = RrdRpn.OP_LOG; | ||
| 67 | else if (expr === 'FLOOR') this.rpnp[steps].op = RrdRpn.OP_FLOOR; | ||
| 68 | else if (expr === 'CEIL') this.rpnp[steps].op = RrdRpn.OP_CEIL; | ||
| 69 | else if (expr === 'EXP') this.rpnp[steps].op = RrdRpn.OP_EXP; | ||
| 70 | else if (expr === 'DUP') this.rpnp[steps].op = RrdRpn.OP_DUP; | ||
| 71 | else if (expr === 'EXC') this.rpnp[steps].op = RrdRpn.OP_EXC; | ||
| 72 | else if (expr === 'POP') this.rpnp[steps].op = RrdRpn.OP_POP; | ||
| 73 | else if (expr === 'LTIME') this.rpnp[steps].op = RrdRpn.OP_LTIME; | ||
| 74 | else if (expr === 'LT') this.rpnp[steps].op = RrdRpn.OP_LT; | ||
| 75 | else if (expr === 'LE') this.rpnp[steps].op = RrdRpn.OP_LE; | ||
| 76 | else if (expr === 'GT') this.rpnp[steps].op = RrdRpn.OP_GT; | ||
| 77 | else if (expr === 'GE') this.rpnp[steps].op = RrdRpn.OP_GE; | ||
| 78 | else if (expr === 'EQ') this.rpnp[steps].op = RrdRpn.OP_EQ; | ||
| 79 | else if (expr === 'IF') this.rpnp[steps].op = RrdRpn.OP_IF; | ||
| 80 | else if (expr === 'MIN') this.rpnp[steps].op = RrdRpn.OP_MIN; | ||
| 81 | else if (expr === 'MAX') this.rpnp[steps].op = RrdRpn.OP_MAX; | ||
| 82 | else if (expr === 'LIMIT') this.rpnp[steps].op = RrdRpn.OP_LIMIT; | ||
| 83 | else if (expr === 'UNKN') this.rpnp[steps].op = RrdRpn.OP_UNKN; | ||
| 84 | else if (expr === 'UN') this.rpnp[steps].op = RrdRpn.OP_UN; | ||
| 85 | else if (expr === 'NEGINF') this.rpnp[steps].op = RrdRpn.OP_NEGINF; | ||
| 86 | else if (expr === 'NE') this.rpnp[steps].op = RrdRpn.OP_NE; | ||
| 87 | else if (expr === 'COUNT') this.rpnp[steps].op = RrdRpn.OP_COUNT; | ||
| 88 | else if (/PREV\([-_A-Za-z0-9]+\)/.test(expr)) { | ||
| 89 | var match = exprs[i].match(/PREV\(([-_A-Za-z0-9]+)\)/i); | ||
| 90 | if (match.length == 2) { | ||
| 91 | this.rpnp[steps].op = RrdRpn.OP_PREV_OTHER; | ||
| 92 | this.rpnp[steps].ptr = this.find_var(gdes, match[1]); // FIXME if -1 | ||
| 93 | } | ||
| 94 | } | ||
| 95 | else if (expr === 'PREV') this.rpnp[steps].op = RrdRpn.OP_PREV; | ||
| 96 | else if (expr === 'INF') this.rpnp[steps].op = RrdRpn.OP_INF; | ||
| 97 | else if (expr === 'ISINF') this.rpnp[steps].op = RrdRpn.OP_ISINF; | ||
| 98 | else if (expr === 'NOW') this.rpnp[steps].op = RrdRpn.OP_NOW; | ||
| 99 | else if (expr === 'TIME') this.rpnp[steps].op = RrdRpn.OP_TIME; | ||
| 100 | else if (expr === 'ATAN2') this.rpnp[steps].op = RrdRpn.OP_ATAN2; | ||
| 101 | else if (expr === 'ATAN') this.rpnp[steps].op = RrdRpn.OP_ATAN; | ||
| 102 | else if (expr === 'SQRT') this.rpnp[steps].op = RrdRpn.OP_SQRT; | ||
| 103 | else if (expr === 'SORT') this.rpnp[steps].op = RrdRpn.OP_SORT; | ||
| 104 | else if (expr === 'REV') this.rpnp[steps].op = RrdRpn.OP_REV; | ||
| 105 | else if (expr === 'TREND') this.rpnp[steps].op = RrdRpn.OP_TREND; | ||
| 106 | else if (expr === 'TRENDNAN') this.rpnp[steps].op = RrdRpn.OP_TRENDNAN; | ||
| 107 | else if (expr === 'PREDICT') this.rpnp[steps].op = RrdRpn.OP_PREDICT; | ||
| 108 | else if (expr === 'PREDICTSIGMA') this.rpnp[steps].op = RrdRpn.OP_PREDICTSIGMA; | ||
| 109 | else if (expr === 'RAD2DEG') this.rpnp[steps].op = RrdRpn.OP_RAD2DEG; | ||
| 110 | else if (expr === 'DEG2RAD') this.rpnp[steps].op = RrdRpn.OP_DEG2RAD; | ||
| 111 | else if (expr === 'AVG') this.rpnp[steps].op = RrdRpn.OP_AVG; | ||
| 112 | else if (expr === 'ABS') this.rpnp[steps].op = RrdRpn.OP_ABS; | ||
| 113 | else if (expr === 'ADDNAN') this.rpnp[steps].op = RrdRpn.OP_ADDNAN; | ||
| 114 | else if (/[-_A-Za-z0-9]+/.test(expr)) { | ||
| 115 | this.rpnp[steps].ptr = this.find_var(gdes, exprs[i]); // FIXME if -1 | ||
| 116 | this.rpnp[steps].op = RrdRpn.OP_VARIABLE; | ||
| 117 | } else { | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | this.rpnp[steps + 1] = {}; | ||
| 122 | this.rpnp[steps + 1].op = RrdRpn.OP_END; | ||
| 123 | }; | ||
| 124 | |||
| 125 | RrdRpn.OP_NUMBER= 0; | ||
| 126 | RrdRpn.OP_VARIABLE = 1; | ||
| 127 | RrdRpn.OP_INF = 2; | ||
| 128 | RrdRpn.OP_PREV = 3; | ||
| 129 | RrdRpn.OP_NEGINF = 4; | ||
| 130 | RrdRpn.OP_UNKN = 5; | ||
| 131 | RrdRpn.OP_NOW = 6; | ||
| 132 | RrdRpn.OP_TIME = 7; | ||
| 133 | RrdRpn.OP_ADD = 8; | ||
| 134 | RrdRpn.OP_MOD = 9; | ||
| 135 | RrdRpn.OP_SUB = 10; | ||
| 136 | RrdRpn.OP_MUL = 11; | ||
| 137 | RrdRpn.OP_DIV = 12; | ||
| 138 | RrdRpn.OP_SIN = 13; | ||
| 139 | RrdRpn.OP_DUP = 14; | ||
| 140 | RrdRpn.OP_EXC = 15; | ||
| 141 | RrdRpn.OP_POP = 16; | ||
| 142 | RrdRpn.OP_COS = 17; | ||
| 143 | RrdRpn.OP_LOG = 18; | ||
| 144 | RrdRpn.OP_EXP = 19; | ||
| 145 | RrdRpn.OP_LT = 20; | ||
| 146 | RrdRpn.OP_LE = 21; | ||
| 147 | RrdRpn.OP_GT = 22; | ||
| 148 | RrdRpn.OP_GE = 23; | ||
| 149 | RrdRpn.OP_EQ = 24; | ||
| 150 | RrdRpn.OP_IF = 25; | ||
| 151 | RrdRpn.OP_MIN = 26; | ||
| 152 | RrdRpn.OP_MAX = 27; | ||
| 153 | RrdRpn.OP_LIMIT = 28; | ||
| 154 | RrdRpn.OP_FLOOR = 29; | ||
| 155 | RrdRpn.OP_CEIL = 30; | ||
| 156 | RrdRpn.OP_UN = 31; | ||
| 157 | RrdRpn.OP_END = 32; | ||
| 158 | RrdRpn.OP_LTIME = 33; | ||
| 159 | RrdRpn.OP_NE = 34; | ||
| 160 | RrdRpn.OP_ISINF = 35; | ||
| 161 | RrdRpn.OP_PREV_OTHER = 36; | ||
| 162 | RrdRpn.OP_COUNT = 37; | ||
| 163 | RrdRpn.OP_ATAN = 38; | ||
| 164 | RrdRpn.OP_SQRT = 39; | ||
| 165 | RrdRpn.OP_SORT = 40; | ||
| 166 | RrdRpn.OP_REV = 41; | ||
| 167 | RrdRpn.OP_TREND = 42; | ||
| 168 | RrdRpn.OP_TRENDNAN = 43; | ||
| 169 | RrdRpn.OP_ATAN2 = 44; | ||
| 170 | RrdRpn.OP_RAD2DEG = 45; | ||
| 171 | RrdRpn.OP_DEG2RAD = 46; | ||
| 172 | RrdRpn.OP_PREDICT = 47; | ||
| 173 | RrdRpn.OP_PREDICTSIGMA = 48; | ||
| 174 | RrdRpn.OP_AVG = 49; | ||
| 175 | RrdRpn.OP_ABS = 50; | ||
| 176 | RrdRpn.OP_ADDNAN = 51 ; | ||
| 177 | |||
| 178 | RrdRpn.prototype.find_var = function(gdes, key) | ||
| 179 | { | ||
| 180 | for (var ii = 0, gdes_c = gdes.length; ii < gdes_c; ii++) { | ||
| 181 | if ((gdes[ii].gf == RrdGraphDesc.GF_DEF || | ||
| 182 | gdes[ii].gf == RrdGraphDesc.GF_VDEF || | ||
| 183 | gdes[ii].gf == RrdGraphDesc.GF_CDEF) | ||
| 184 | && gdes[ii].vname == key) { | ||
| 185 | return ii; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | return -1; | ||
| 189 | }; | ||
| 190 | |||
| 191 | RrdRpn.prototype.compare_double = function(x, y) | ||
| 192 | { | ||
| 193 | var diff = x - y; | ||
| 194 | return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; | ||
| 195 | }; | ||
| 196 | |||
| 197 | RrdRpn.prototype.fmod = function (x, y) | ||
| 198 | { | ||
| 199 | // http://kevin.vanzonneveld.net | ||
| 200 | // + original by: Onno Marsman | ||
| 201 | // + input by: Brett Zamir (http://brett-zamir.me) | ||
| 202 | // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) | ||
| 203 | // * example 1: fmod(5.7, 1.3); | ||
| 204 | // * returns 1: 0.5 | ||
| 205 | var tmp, tmp2, p = 0, | ||
| 206 | pY = 0, | ||
| 207 | l = 0.0, | ||
| 208 | l2 = 0.0; | ||
| 209 | |||
| 210 | tmp = x.toExponential().match(/^.\.?(.*)e(.+)$/); | ||
| 211 | p = parseInt(tmp[2], 10) - (tmp[1] + '').length; | ||
| 212 | tmp = y.toExponential().match(/^.\.?(.*)e(.+)$/); | ||
| 213 | pY = parseInt(tmp[2], 10) - (tmp[1] + '').length; | ||
| 214 | |||
| 215 | if (pY > p) p = pY; | ||
| 216 | |||
| 217 | tmp2 = (x % y); | ||
| 218 | |||
| 219 | if (p < -100 || p > 20) { | ||
| 220 | l = Math.round(Math.log(tmp2) / Math.log(10)); | ||
| 221 | l2 = Math.pow(10, l); | ||
| 222 | return (tmp2 / l2).toFixed(l - p) * l2; | ||
| 223 | } else { | ||
| 224 | return parseFloat(tmp2.toFixed(-p)); | ||
| 225 | } | ||
| 226 | }; | ||
| 227 | |||
| 228 | RrdRpn.prototype.calc = function (data_idx, output, output_idx) | ||
| 229 | { | ||
| 230 | var stptr = -1; | ||
| 231 | |||
| 232 | this.rpnstack = []; | ||
| 233 | |||
| 234 | for (var rpi = 0; this.rpnp[rpi].op != RrdRpn.OP_END; rpi++) { | ||
| 235 | switch (this.rpnp[rpi].op) { | ||
| 236 | case RrdRpn.OP_NUMBER: | ||
| 237 | this.rpnstack[++stptr] = this.rpnp[rpi].val; | ||
| 238 | break; | ||
| 239 | case RrdRpn.OP_VARIABLE: | ||
| 240 | case RrdRpn.OP_PREV_OTHER: | ||
| 241 | if (this.rpnp[rpi].ds_cnt == 0) { | ||
| 242 | throw new RrdRpnError("VDEF made it into rpn_calc... aborting"); | ||
| 243 | } else { | ||
| 244 | if (this.rpnp[rpi].op == RrdRpn.OP_VARIABLE) { | ||
| 245 | this.rpnstack[++stptr] = this.rpnp[rpi].data[this.rpnp[rpi].pdata]; | ||
| 246 | } else { | ||
| 247 | if ((output_idx) <= 0) this.rpnstack[++stptr] = Number.NaN; | ||
| 248 | else this.rpnstack[++stptr] = this.rpnp[rpi].data[this.rpnp[rpi].pdata - this.rpnp[rpi].ds_cnt]; | ||
| 249 | } | ||
| 250 | if (data_idx % this.rpnp[rpi].step == 0) { | ||
| 251 | this.rpnp[rpi].pdata += this.rpnp[rpi].ds_cnt; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | break; | ||
| 255 | case RrdRpn.OP_COUNT: | ||
| 256 | this.rpnstack[++stptr] = (output_idx + 1); /* Note: Counter starts at 1 */ | ||
| 257 | break; | ||
| 258 | case RrdRpn.OP_PREV: | ||
| 259 | if ((output_idx) <= 0) this.rpnstack[++stptr] = Number.NaN; | ||
| 260 | else this.rpnstack[++stptr] = output[output_idx - 1]; | ||
| 261 | break; | ||
| 262 | case RrdRpn.OP_UNKN: | ||
| 263 | this.rpnstack[++stptr] = Number.NaN; | ||
| 264 | break; | ||
| 265 | case RrdRpn.OP_INF: | ||
| 266 | this.rpnstack[++stptr] = Infinity; | ||
| 267 | break; | ||
| 268 | case RrdRpn.OP_NEGINF: | ||
| 269 | this.rpnstack[++stptr] = -Infinity; | ||
| 270 | break; | ||
| 271 | case RrdRpn.OP_NOW: | ||
| 272 | this.rpnstack[++stptr] = Math.round((new Date()).getTime() / 1000); | ||
| 273 | break; | ||
| 274 | case RrdRpn.OP_TIME: | ||
| 275 | this.rpnstack[++stptr] = data_idx; | ||
| 276 | break; | ||
| 277 | case RrdRpn.OP_LTIME: | ||
| 278 | var date = new Date(data_idx*1000); // FIXME XXX | ||
| 279 | this.rpnstack[++stptr] = date.getTimezoneOffset() * 60 + data_idx; | ||
| 280 | break; | ||
| 281 | case RrdRpn.OP_ADD: | ||
| 282 | if(stptr < 1) throw new RrdRpnError(); | ||
| 283 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] + this.rpnstack[stptr]; | ||
| 284 | stptr--; | ||
| 285 | break; | ||
| 286 | case RrdRpn.OP_ADDNAN: | ||
| 287 | if(stptr < 1) throw new RrdRpnError(); | ||
| 288 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 289 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 290 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 291 | /* NOOP */ | ||
| 292 | /* this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1]; */ | ||
| 293 | } else { | ||
| 294 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] + this.rpnstack[stptr]; | ||
| 295 | } | ||
| 296 | stptr--; | ||
| 297 | break; | ||
| 298 | case RrdRpn.OP_SUB: | ||
| 299 | if(stptr < 1) throw new RrdRpnError(); | ||
| 300 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] - this.rpnstack[stptr]; | ||
| 301 | stptr--; | ||
| 302 | break; | ||
| 303 | case RrdRpn.OP_MUL: | ||
| 304 | if(stptr < 1) throw new RrdRpnError(); | ||
| 305 | this.rpnstack[stptr - 1] = (this.rpnstack[stptr - 1]) * (this.rpnstack[stptr]); | ||
| 306 | stptr--; | ||
| 307 | break; | ||
| 308 | case RrdRpn.OP_DIV: | ||
| 309 | if(stptr < 1) throw new RrdRpnError(); | ||
| 310 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] / this.rpnstack[stptr]; | ||
| 311 | stptr--; | ||
| 312 | break; | ||
| 313 | case RrdRpn.OP_MOD: | ||
| 314 | if(stptr < 1) throw new RrdRpnError(); | ||
| 315 | this.rpnstack[stptr - 1] = this.fmod(this.rpnstack[stptr - 1] , this.rpnstack[stptr]); | ||
| 316 | stptr--; | ||
| 317 | break; | ||
| 318 | case RrdRpn.OP_SIN: | ||
| 319 | if(stptr < 0) throw new RrdRpnError(); | ||
| 320 | this.rpnstack[stptr] = Math.sin(this.rpnstack[stptr]); | ||
| 321 | break; | ||
| 322 | case RrdRpn.OP_ATAN: | ||
| 323 | if(stptr < 0) throw new RrdRpnError(); | ||
| 324 | this.rpnstack[stptr] = Math.atan(this.rpnstack[stptr]); | ||
| 325 | break; | ||
| 326 | case RrdRpn.OP_RAD2DEG: | ||
| 327 | if(stptr < 0) throw new RrdRpnError(); | ||
| 328 | this.rpnstack[stptr] = 57.29577951 * this.rpnstack[stptr]; | ||
| 329 | break; | ||
| 330 | case RrdRpn.OP_DEG2RAD: | ||
| 331 | if(stptr < 0) throw new RrdRpnError(); | ||
| 332 | this.rpnstack[stptr] = 0.0174532952 * this.rpnstack[stptr]; | ||
| 333 | break; | ||
| 334 | case RrdRpn.OP_ATAN2: | ||
| 335 | if(stptr < 1) throw new RrdRpnError(); | ||
| 336 | this.rpnstack[stptr - 1] = Math.atan2(this.rpnstack[stptr - 1], this.rpnstack[stptr]); | ||
| 337 | stptr--; | ||
| 338 | break; | ||
| 339 | case RrdRpn.OP_COS: | ||
| 340 | if(stptr < 0) throw new RrdRpnError(); | ||
| 341 | this.rpnstack[stptr] = Math.cos(this.rpnstack[stptr]); | ||
| 342 | break; | ||
| 343 | case RrdRpn.OP_CEIL: | ||
| 344 | if(stptr < 0) throw new RrdRpnError(); | ||
| 345 | this.rpnstack[stptr] = Math.ceil(this.rpnstack[stptr]); | ||
| 346 | break; | ||
| 347 | case RrdRpn.OP_FLOOR: | ||
| 348 | if(stptr < 0) throw new RrdRpnError(); | ||
| 349 | this.rpnstack[stptr] = Math.floor(this.rpnstack[stptr]); | ||
| 350 | break; | ||
| 351 | case RrdRpn.OP_LOG: | ||
| 352 | if(stptr < 0) throw new RrdRpnError(); | ||
| 353 | this.rpnstack[stptr] = Math.log(this.rpnstack[stptr]); | ||
| 354 | break; | ||
| 355 | case RrdRpn.OP_DUP: | ||
| 356 | if(stptr < 0) throw new RrdRpnError(); | ||
| 357 | this.rpnstack[stptr + 1] = this.rpnstack[stptr]; | ||
| 358 | stptr++; | ||
| 359 | break; | ||
| 360 | case RrdRpn.OP_POP: | ||
| 361 | if(stptr < 0) throw new RrdRpnError(); | ||
| 362 | stptr--; | ||
| 363 | break; | ||
| 364 | case RrdRpn.OP_EXC: | ||
| 365 | if(stptr < 1) throw new RrdRpnError(); { | ||
| 366 | var dummy = this.rpnstack[stptr]; | ||
| 367 | this.rpnstack[stptr] = this.rpnstack[stptr - 1]; | ||
| 368 | this.rpnstack[stptr - 1] = dummy; | ||
| 369 | } | ||
| 370 | break; | ||
| 371 | case RrdRpn.OP_EXP: | ||
| 372 | if(stptr < 0) throw new RrdRpnError(); | ||
| 373 | this.rpnstack[stptr] = Math.exp(this.rpnstack[stptr]); | ||
| 374 | break; | ||
| 375 | case RrdRpn.OP_LT: | ||
| 376 | if(stptr < 1) throw new RrdRpnError(); | ||
| 377 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 378 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 379 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 380 | } else { | ||
| 381 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] < this.rpnstack[stptr] ? 1.0 : 0.0; | ||
| 382 | } | ||
| 383 | stptr--; | ||
| 384 | break; | ||
| 385 | case RrdRpn.OP_LE: | ||
| 386 | if(stptr < 1) throw new RrdRpnError(); | ||
| 387 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 388 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 389 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 390 | } else { | ||
| 391 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] <= this.rpnstack[stptr] ? 1.0 : 0.0; | ||
| 392 | } | ||
| 393 | stptr--; | ||
| 394 | break; | ||
| 395 | case RrdRpn.OP_GT: | ||
| 396 | if(stptr < 1) throw new RrdRpnError(); | ||
| 397 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 398 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 399 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 400 | } else { | ||
| 401 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] > this.rpnstack[stptr] ? 1.0 : 0.0; | ||
| 402 | } | ||
| 403 | stptr--; | ||
| 404 | break; | ||
| 405 | case RrdRpn.OP_GE: | ||
| 406 | if(stptr < 1) throw new RrdRpnError(); | ||
| 407 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 408 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 409 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 410 | } else { | ||
| 411 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] >= this.rpnstack[stptr] ? 1.0 : 0.0; | ||
| 412 | } | ||
| 413 | stptr--; | ||
| 414 | break; | ||
| 415 | case RrdRpn.OP_NE: | ||
| 416 | if(stptr < 1) throw new RrdRpnError(); | ||
| 417 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 418 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 419 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 420 | } else { | ||
| 421 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] == this.rpnstack[stptr] ? 0.0 : 1.0; | ||
| 422 | } | ||
| 423 | stptr--; | ||
| 424 | break; | ||
| 425 | case RrdRpn.OP_EQ: | ||
| 426 | if(stptr < 1) throw new RrdRpnError(); | ||
| 427 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 428 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 429 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 430 | } else { | ||
| 431 | this.rpnstack[stptr - 1] = this.rpnstack[stptr - 1] == this.rpnstack[stptr] ? 1.0 : 0.0; | ||
| 432 | } | ||
| 433 | stptr--; | ||
| 434 | break; | ||
| 435 | case RrdRpn.OP_IF: | ||
| 436 | if(stptr < 2) throw new RrdRpnError(); | ||
| 437 | this.rpnstack[stptr - 2] = (isNaN(this.rpnstack[stptr - 2]) || this.rpnstack[stptr - 2] == 0.0) ? this.rpnstack[stptr] : this.rpnstack[stptr - 1]; | ||
| 438 | stptr--; | ||
| 439 | stptr--; | ||
| 440 | break; | ||
| 441 | case RrdRpn.OP_MIN: | ||
| 442 | if(stptr < 1) throw new RrdRpnError(); | ||
| 443 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 444 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 445 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 446 | } else if (this.rpnstack[stptr - 1] > this.rpnstack[stptr]) { | ||
| 447 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 448 | } | ||
| 449 | stptr--; | ||
| 450 | break; | ||
| 451 | case RrdRpn.OP_MAX: | ||
| 452 | if(stptr < 1) throw new RrdRpnError(); | ||
| 453 | if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 454 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 455 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 456 | } else if (this.rpnstack[stptr - 1] < this.rpnstack[stptr]) { | ||
| 457 | this.rpnstack[stptr - 1] = this.rpnstack[stptr]; | ||
| 458 | } | ||
| 459 | stptr--; | ||
| 460 | break; | ||
| 461 | case RrdRpn.OP_LIMIT: | ||
| 462 | if(stptr < 2) throw new RrdRpnError(); | ||
| 463 | if (isNaN(this.rpnstack[stptr - 2])) { | ||
| 464 | } else if (isNaN(this.rpnstack[stptr - 1])) { | ||
| 465 | this.rpnstack[stptr - 2] = this.rpnstack[stptr - 1]; | ||
| 466 | } else if (isNaN(this.rpnstack[stptr])) { | ||
| 467 | this.rpnstack[stptr - 2] = this.rpnstack[stptr]; | ||
| 468 | } else if (this.rpnstack[stptr - 2] < this.rpnstack[stptr - 1]) { | ||
| 469 | this.rpnstack[stptr - 2] = Number.NaN; | ||
| 470 | } else if (this.rpnstack[stptr - 2] > this.rpnstack[stptr]) { | ||
| 471 | this.rpnstack[stptr - 2] = Number.NaN; | ||
| 472 | } | ||
| 473 | stptr -= 2; | ||
| 474 | break; | ||
| 475 | case RrdRpn.OP_UN: | ||
| 476 | if(stptr < 0) throw new RrdRpnError(); | ||
| 477 | this.rpnstack[stptr] = isNaN(this.rpnstack[stptr]) ? 1.0 : 0.0; | ||
| 478 | break; | ||
| 479 | case RrdRpn.OP_ISINF: | ||
| 480 | if(stptr < 0) throw new RrdRpnError(); | ||
| 481 | this.rpnstack[stptr] = isInfinite(this.rpnstack[stptr]) ? 1.0 : 0.0; | ||
| 482 | break; | ||
| 483 | case RrdRpn.OP_SQRT: | ||
| 484 | if(stptr < 0) throw new RrdRpnError(); | ||
| 485 | this.rpnstack[stptr] = Math.sqrt(this.rpnstack[stptr]); | ||
| 486 | break; | ||
| 487 | case RrdRpn.OP_SORT: | ||
| 488 | if(stptr < 0) throw new RrdRpnError(); | ||
| 489 | var spn = this.rpnstack[stptr--]; | ||
| 490 | if(stptr < spn - 1) throw new RrdRpnError(); | ||
| 491 | var array = this.rpnstack.slice(stptr - spn + 1, stptr +1); | ||
| 492 | array.sort(this.compare_double); | ||
| 493 | for (var i=stptr - spn + 1, ii=0; i < (stptr +1) ; i++, ii++) | ||
| 494 | this.rpnstack[i] = array[ii]; | ||
| 495 | // qsort(this.rpnstack + stptr - spn + 1, spn, sizeof(double), rpn_compare_double); | ||
| 496 | break; | ||
| 497 | case RrdRpn.OP_REV: | ||
| 498 | if(stptr < 0) throw new RrdRpnError(); | ||
| 499 | var spn = this.rpnstack[stptr--]; | ||
| 500 | if(stptr < spn - 1) throw new RrdRpnError(); | ||
| 501 | var array = this.rpnstack.slice(stptr - spn + 1, stptr +1); | ||
| 502 | array.reverse(); | ||
| 503 | for (var i=stptr - spn + 1, ii=0; i < (stptr +1) ; i++, ii++) | ||
| 504 | this.rpnstack[i] = array[ii]; | ||
| 505 | break; | ||
| 506 | case RrdRpn.OP_PREDICT: | ||
| 507 | case RrdRpn.OP_PREDICTSIGMA: | ||
| 508 | if(stptr < 2) throw new RrdRpnError(); | ||
| 509 | var locstepsize = this.rpnstack[--stptr]; | ||
| 510 | var shifts = this.rpnstack[--stptr]; | ||
| 511 | if(stptr < shifts) throw new RrdRpnError(); | ||
| 512 | if (shifts<0) stptr--; | ||
| 513 | else stptr-=shifts; | ||
| 514 | var val=Number.NaN; | ||
| 515 | var dsstep = this.rpnp[rpi - 1].step; | ||
| 516 | var dscount = this.rpnp[rpi - 1].ds_cnt; | ||
| 517 | var locstep = Math.ceil(locstepsize/dsstep); | ||
| 518 | var sum = 0; | ||
| 519 | var sum2 = 0; | ||
| 520 | var count = 0; | ||
| 521 | /* now loop for each position */ | ||
| 522 | var doshifts=shifts; | ||
| 523 | if (shifts<0) doshifts=-shifts; | ||
| 524 | for(var loop=0;loop<doshifts;loop++) { | ||
| 525 | var shiftstep=1; | ||
| 526 | if (shifts<0) shiftstep = loop*this.rpnstack[stptr]; | ||
| 527 | else shiftstep = this.rpnstack[stptr+loop]; | ||
| 528 | if(shiftstep <0) { | ||
| 529 | throw new RrdRpnError("negative shift step not allowed: "+shiftstep); | ||
| 530 | } | ||
| 531 | shiftstep=Math.ceil(shiftstep/dsstep); | ||
| 532 | for(var i=0;i<=locstep;i++) { | ||
| 533 | var offset=shiftstep+i; | ||
| 534 | if ((offset>=0)&&(offset<output_idx)) { | ||
| 535 | val = this.rpnp[rpi - 1].data[-dscount * offset]; | ||
| 536 | if (! isNaN(val)) { | ||
| 537 | sum+=val; | ||
| 538 | sum2+=val*val; | ||
| 539 | count++; | ||
| 540 | } | ||
| 541 | } | ||
| 542 | } | ||
| 543 | } | ||
| 544 | val=Number.NaN; | ||
| 545 | if (this.rpnp[rpi].op == RrdRpn.OP_PREDICT) { | ||
| 546 | if (count>0) val = sum/count; | ||
| 547 | } else { | ||
| 548 | if (count>1) { | ||
| 549 | val=count*sum2-sum*sum; | ||
| 550 | if (val<0) { | ||
| 551 | val=Number.NaN; | ||
| 552 | } else { | ||
| 553 | val=Math.sqrt(val/(count*(count-1.0))); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | } | ||
| 557 | this.rpnstack[stptr] = val; | ||
| 558 | break; | ||
| 559 | case RrdRpn.OP_TREND: | ||
| 560 | case RrdRpn.OP_TRENDNAN: | ||
| 561 | if(stptr < 1) throw new RrdRpnError(); | ||
| 562 | if ((rpi < 2) || (this.rpnp[rpi - 2].op != RrdRpn.OP_VARIABLE)) { | ||
| 563 | throw new RrdRpnError("malformed trend arguments"); | ||
| 564 | } else { | ||
| 565 | var dur = this.rpnstack[stptr]; | ||
| 566 | var step = this.rpnp[rpi - 2].step; | ||
| 567 | |||
| 568 | if (output_idx + 1 >= Math.ceil(dur / step)) { | ||
| 569 | var ignorenan = (this.rpnp[rpi].op == RrdRpn.OP_TREND); | ||
| 570 | var accum = 0.0; | ||
| 571 | var i = 0; | ||
| 572 | var count = 0; | ||
| 573 | |||
| 574 | do { | ||
| 575 | var val = this.rpnp[rpi - 2].data[this.rpnp[rpi - 2].ds_cnt * i--]; | ||
| 576 | if (ignorenan || !isNaN(val)) { | ||
| 577 | accum += val; | ||
| 578 | ++count; | ||
| 579 | } | ||
| 580 | dur -= step; | ||
| 581 | } while (dur > 0); | ||
| 582 | |||
| 583 | this.rpnstack[--stptr] = (count == 0) ? Number.NaN : (accum / count); | ||
| 584 | } else this.rpnstack[--stptr] = Number.NaN; | ||
| 585 | } | ||
| 586 | break; | ||
| 587 | case RrdRpn.OP_AVG: | ||
| 588 | if(stptr < 0) throw new RrdRpnError(); | ||
| 589 | var i = this.rpnstack[stptr--]; | ||
| 590 | var sum = 0; | ||
| 591 | var count = 0; | ||
| 592 | |||
| 593 | if(stptr < i - 1) throw new RrdRpnError(); | ||
| 594 | while (i > 0) { | ||
| 595 | var val = this.rpnstack[stptr--]; | ||
| 596 | i--; | ||
| 597 | if (isNaN(val)) continue; | ||
| 598 | count++; | ||
| 599 | sum += val; | ||
| 600 | } | ||
| 601 | if (count > 0) this.rpnstack[++stptr] = sum / count; | ||
| 602 | else this.rpnstack[++stptr] = Number.NaN; | ||
| 603 | break; | ||
| 604 | case RrdRpn.OP_ABS: | ||
| 605 | if(stptr < 0) throw new RrdRpnError(); | ||
| 606 | this.rpnstack[stptr] = fabs(this.rpnstack[stptr]); | ||
| 607 | break; | ||
| 608 | case RrdRpn.OP_END: | ||
| 609 | break; | ||
| 610 | } | ||
| 611 | } | ||
| 612 | if (stptr != 0) throw new RrdRpnError("RPN final stack size != 1"); | ||
| 613 | output[output_idx] = this.rpnstack[0]; | ||
| 614 | return 0; | ||
| 615 | }; | ||
| 616 | |||
diff --git a/js/RrdTime.js b/js/RrdTime.js new file mode 100644 index 0000000..95ea0ae --- /dev/null +++ b/js/RrdTime.js | |||
| @@ -0,0 +1,621 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010 | ||
| 18 | * | ||
| 19 | * Convert to javascript: Manuel Sanmartin <manuel.luis at gmail.com> | ||
| 20 | **/ | ||
| 21 | |||
| 22 | "use strict"; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * RrdTimeError | ||
| 26 | * @constructor | ||
| 27 | */ | ||
| 28 | var RrdTimeError = function (message) | ||
| 29 | { | ||
| 30 | this.prototype = Error.prototype; | ||
| 31 | this.name = "RrdTimeError"; | ||
| 32 | this.message = (message) ? message : "Error"; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * RrdTime | ||
| 37 | * @constructor | ||
| 38 | */ | ||
| 39 | var RrdTime = function(tspec) /* parser */ | ||
| 40 | { | ||
| 41 | var date = new Date(); | ||
| 42 | var hr = 0; | ||
| 43 | |||
| 44 | this.tspec = tspec; | ||
| 45 | |||
| 46 | this.tokens = (tspec+'').match(/[0-9]+|[A-Za-z]+|[:.+-\/]/g); | ||
| 47 | this.toklen = this.tokens.length; | ||
| 48 | this.tokidx = 0; | ||
| 49 | |||
| 50 | this.token = null; | ||
| 51 | this.tokid = 0; | ||
| 52 | |||
| 53 | this.specials = RrdTime.VARIOUSWORDS; | ||
| 54 | |||
| 55 | /* establish the default time reference */ | ||
| 56 | this.type = RrdTime.ABSOLUTE_TIME; | ||
| 57 | this.offset = 0; | ||
| 58 | this.tm_sec = date.getSeconds(); | ||
| 59 | this.tm_min = date.getMinutes(); | ||
| 60 | this.tm_hour = date.getHours(); | ||
| 61 | this.tm_mday = date.getDate(); | ||
| 62 | this.tm_mon = date.getMonth(); | ||
| 63 | this.tm_year = date.getFullYear()-1900; | ||
| 64 | this.tm_wday = date.getDay(); | ||
| 65 | |||
| 66 | this.gettok(); | ||
| 67 | switch (this.tokid) { | ||
| 68 | case RrdTime.PLUS: | ||
| 69 | case RrdTime.MINUS: | ||
| 70 | break; /* jump to OFFSET-SPEC part */ | ||
| 71 | case RrdTime.EPOCH: | ||
| 72 | this.type = RrdTime.RELATIVE_TO_EPOCH; | ||
| 73 | case RrdTime.START: | ||
| 74 | case RrdTime.END: | ||
| 75 | if (this.tokid === RrdTime.EPOCH) | ||
| 76 | this.type = RrdTime.RELATIVE_TO_START_TIME; | ||
| 77 | else | ||
| 78 | this.type = RrdTime.RELATIVE_TO_END_TIME; | ||
| 79 | this.tm_sec = 0; | ||
| 80 | this.tm_min = 0; | ||
| 81 | this.tm_hour = 0; | ||
| 82 | this.tm_mday = 0; | ||
| 83 | this.tm_mon = 0; | ||
| 84 | this.tm_year = 0; | ||
| 85 | case RrdTime.NOW: | ||
| 86 | var time_reference = this.tokid; | ||
| 87 | this.gettok(); | ||
| 88 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) | ||
| 89 | break; | ||
| 90 | if (time_reference != RrdTime.NOW) { | ||
| 91 | throw new RrdTimeError("'start' or 'end' MUST be followed by +|- offset"); | ||
| 92 | } else if (this.tokid != RrdTime.EOF) { | ||
| 93 | throw new RrdTimeError("if 'now' is followed by a token it must be +|- offset"); | ||
| 94 | } | ||
| 95 | break; | ||
| 96 | case RrdTime.NUMBER: /* Only absolute time specifications below */ | ||
| 97 | var hour_sv = this.tm_hour; | ||
| 98 | var year_sv = this.tm_year; | ||
| 99 | this.tm_hour = 30; | ||
| 100 | this.tm_year = 30000; | ||
| 101 | this.tod(); | ||
| 102 | this.day(); | ||
| 103 | if (this.tm_hour == 30 && this.tm_year != 30000) | ||
| 104 | this.tod(); | ||
| 105 | if (this.tm_hour == 30) | ||
| 106 | this.tm_hour = hour_sv; | ||
| 107 | if (this.tm_year == 30000) | ||
| 108 | this.tm_year = year_sv; | ||
| 109 | break; | ||
| 110 | case RrdTime.JAN: | ||
| 111 | case RrdTime.FEB: | ||
| 112 | case RrdTime.MAR: | ||
| 113 | case RrdTime.APR: | ||
| 114 | case RrdTime.MAY: | ||
| 115 | case RrdTime.JUN: | ||
| 116 | case RrdTime.JUL: | ||
| 117 | case RrdTime.AUG: | ||
| 118 | case RrdTime.SEP: | ||
| 119 | case RrdTime.OCT: | ||
| 120 | case RrdTime.NOV: | ||
| 121 | case RrdTime.DEC: | ||
| 122 | this.day(); | ||
| 123 | if (this.tokid != RrdTime.NUMBER) | ||
| 124 | break; | ||
| 125 | this.tod(); | ||
| 126 | break; | ||
| 127 | case RrdTime.TEATIME: | ||
| 128 | hr += 4; | ||
| 129 | case RrdTime.NOON: | ||
| 130 | hr += 12; | ||
| 131 | case RrdTime.MIDNIGHT: | ||
| 132 | this.tm_hour = hr; | ||
| 133 | this.tm_min = 0; | ||
| 134 | this.tm_sec = 0; | ||
| 135 | this.gettok(); | ||
| 136 | this.day(); | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | throw new RrdTimeError("unparsable time: "+this.token+" "+this.sct); | ||
| 140 | break; | ||
| 141 | } /* ugly case statement */ | ||
| 142 | |||
| 143 | /* | ||
| 144 | * the OFFSET-SPEC part | ||
| 145 | * (NOTE, the sc_tokid was prefetched for us by the previous code) | ||
| 146 | */ | ||
| 147 | if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) { | ||
| 148 | this.specials = RrdTime.TIMEMULTIPLIERS; /* switch special words context */ | ||
| 149 | while (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS || this.tokid == RrdTime.NUMBER) { | ||
| 150 | if (this.tokid == RrdTime.NUMBER) { | ||
| 151 | this.plus_minus(-1); | ||
| 152 | } else { | ||
| 153 | this.plus_minus(this.tokid); | ||
| 154 | } | ||
| 155 | this.gettok(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */ | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /* now we should be at EOF */ | ||
| 160 | if (this.tokid != RrdTime.EOF) | ||
| 161 | throw new RrdTimeError("unparsable trailing text: '..."+this.token+"'"); | ||
| 162 | // if (this.type == RrdTime.ABSOLUTE_TIME) | ||
| 163 | // if (mktime(&ptv->tm) == -1) // FIXME ?? | ||
| 164 | // panic(e("the specified time is incorrect (out of range?)")); | ||
| 165 | }; | ||
| 166 | |||
| 167 | RrdTime.EOF = -1; | ||
| 168 | RrdTime.MIDNIGHT = 0; | ||
| 169 | RrdTime.NOON = 1; | ||
| 170 | RrdTime.TEATIME = 2; | ||
| 171 | RrdTime.PM = 3; | ||
| 172 | RrdTime.AM = 4; | ||
| 173 | RrdTime.YESTERDAY = 5; | ||
| 174 | RrdTime.TODAY = 6; | ||
| 175 | RrdTime.TOMORROW = 7; | ||
| 176 | RrdTime.NOW = 8; | ||
| 177 | RrdTime.START = 9; | ||
| 178 | RrdTime.END = 10; | ||
| 179 | RrdTime.EPOCH = 11; | ||
| 180 | RrdTime.SECONDS = 12; | ||
| 181 | RrdTime.MINUTES = 13; | ||
| 182 | RrdTime.HOURS = 14; | ||
| 183 | RrdTime.DAYS = 15; | ||
| 184 | RrdTime.WEEKS = 16; | ||
| 185 | RrdTime.MONTHS = 17; | ||
| 186 | RrdTime.YEARS = 18; | ||
| 187 | RrdTime.MONTHS_MINUTES = 19; | ||
| 188 | RrdTime.NUMBER = 20; | ||
| 189 | RrdTime.PLUS = 21; | ||
| 190 | RrdTime.MINUS = 22; | ||
| 191 | RrdTime.DOT = 23; | ||
| 192 | RrdTime.COLON = 24; | ||
| 193 | RrdTime.SLASH = 25; | ||
| 194 | RrdTime.ID = 26; | ||
| 195 | RrdTime.JUNK = 27; | ||
| 196 | RrdTime.JAN = 28; | ||
| 197 | RrdTime.FEB = 29; | ||
| 198 | RrdTime.MAR = 30; | ||
| 199 | RrdTime.APR = 31; | ||
| 200 | RrdTime.MAY = 32; | ||
| 201 | RrdTime.JUN = 33; | ||
| 202 | RrdTime.JUL = 34; | ||
| 203 | RrdTime.AUG = 35; | ||
| 204 | RrdTime.SEP = 36; | ||
| 205 | RrdTime.OCT = 37; | ||
| 206 | RrdTime.NOV = 38; | ||
| 207 | RrdTime.DEC = 39; | ||
| 208 | RrdTime.SUN = 40; | ||
| 209 | RrdTime.MON = 41; | ||
| 210 | RrdTime.TUE = 42; | ||
| 211 | RrdTime.WED = 43; | ||
| 212 | RrdTime.THU = 44; | ||
| 213 | RrdTime.FRI = 45; | ||
| 214 | RrdTime.SAT = 46; | ||
| 215 | |||
| 216 | RrdTime.VARIOUSWORDS = { | ||
| 217 | "midnight": RrdTime.MIDNIGHT, /* 00:00:00 of today or tomorrow */ | ||
| 218 | "noon": RrdTime.NOON, /* 12:00:00 of today or tomorrow */ | ||
| 219 | "teatime": RrdTime.TEATIME, /* 16:00:00 of today or tomorrow */ | ||
| 220 | "am": RrdTime.AM, /* morning times for 0-12 clock */ | ||
| 221 | "pm": RrdTime.PM, /* evening times for 0-12 clock */ | ||
| 222 | "tomorrow": RrdTime.TOMORROW, | ||
| 223 | "yesterday": RrdTime.YESTERDAY, | ||
| 224 | "today": RrdTime.TODAY, | ||
| 225 | "now": RrdTime.NOW, | ||
| 226 | "n": RrdTime.NOW, | ||
| 227 | "start": RrdTime.START, | ||
| 228 | "s": RrdTime.START, | ||
| 229 | "end": RrdTime.END, | ||
| 230 | "e": RrdTime.END, | ||
| 231 | "epoch": RrdTime.EPOCH, | ||
| 232 | "jan": RrdTime.JAN, | ||
| 233 | "feb": RrdTime.FEB, | ||
| 234 | "mar": RrdTime.MAR, | ||
| 235 | "apr": RrdTime.APR, | ||
| 236 | "may": RrdTime.MAY, | ||
| 237 | "jun": RrdTime.JUN, | ||
| 238 | "jul": RrdTime.JUL, | ||
| 239 | "aug": RrdTime.AUG, | ||
| 240 | "sep": RrdTime.SEP, | ||
| 241 | "oct": RrdTime.OCT, | ||
| 242 | "nov": RrdTime.NOV, | ||
| 243 | "dec": RrdTime.DEC, | ||
| 244 | "january": RrdTime.JAN, | ||
| 245 | "february": RrdTime.FEB, | ||
| 246 | "march": RrdTime.MAR, | ||
| 247 | "april": RrdTime.APR, | ||
| 248 | // "may": RrdTime.MAY, | ||
| 249 | "june": RrdTime.JUN, | ||
| 250 | "july": RrdTime.JUL, | ||
| 251 | "august": RrdTime.AUG, | ||
| 252 | "september": RrdTime.SEP, | ||
| 253 | "october": RrdTime.OCT, | ||
| 254 | "november": RrdTime.NOV, | ||
| 255 | "december": RrdTime.DEC, | ||
| 256 | "sunday": RrdTime.SUN, | ||
| 257 | "sun": RrdTime.SUN, | ||
| 258 | "monday": RrdTime.MON, | ||
| 259 | "mon": RrdTime.MON, | ||
| 260 | "tuesday": RrdTime.TUE, | ||
| 261 | "tue": RrdTime.TUE, | ||
| 262 | "wednesday": RrdTime.WED, | ||
| 263 | "wed": RrdTime.WED, | ||
| 264 | "thursday": RrdTime.THU, | ||
| 265 | "thu": RrdTime.THU, | ||
| 266 | "friday": RrdTime.FRI, | ||
| 267 | "fri": RrdTime.FRI, | ||
| 268 | "saturday": RrdTime.SAT, | ||
| 269 | "sat": RrdTime.SAT | ||
| 270 | }; | ||
| 271 | |||
| 272 | RrdTime.TIMEMULTIPLIERS = { | ||
| 273 | "second": RrdTime.SECONDS, /* seconds multiplier */ | ||
| 274 | "seconds": RrdTime.SECONDS, /* (pluralized) */ | ||
| 275 | "sec": RrdTime.SECONDS, /* (generic) */ | ||
| 276 | "s": RrdTime.SECONDS, /* (short generic) */ | ||
| 277 | "minute": RrdTime.MINUTES, /* minutes multiplier */ | ||
| 278 | "minutes": RrdTime.MINUTES, /* (pluralized) */ | ||
| 279 | "min": RrdTime.MINUTES, /* (generic) */ | ||
| 280 | "m": RrdTime.MONTHS_MINUTES, /* (short generic) */ | ||
| 281 | "hour": RrdTime.HOURS, /* hours ... */ | ||
| 282 | "hours": RrdTime.HOURS, /* (pluralized) */ | ||
| 283 | "hr": RrdTime.HOURS, /* (generic) */ | ||
| 284 | "h": RrdTime.HOURS, /* (short generic) */ | ||
| 285 | "day": RrdTime.DAYS, /* days ... */ | ||
| 286 | "days": RrdTime.DAYS, /* (pluralized) */ | ||
| 287 | "d": RrdTime.DAYS, /* (short generic) */ | ||
| 288 | "week": RrdTime.WEEKS, /* week ... */ | ||
| 289 | "weeks": RrdTime.WEEKS, /* (pluralized) */ | ||
| 290 | "wk": RrdTime.WEEKS, /* (generic) */ | ||
| 291 | "w": RrdTime.WEEKS, /* (short generic) */ | ||
| 292 | "month": RrdTime.MONTHS, /* week ... */ | ||
| 293 | "months": RrdTime.MONTHS, /* (pluralized) */ | ||
| 294 | "mon": RrdTime.MONTHS, /* (generic) */ | ||
| 295 | "year": RrdTime.YEARS, /* year ... */ | ||
| 296 | "years": RrdTime.YEARS, /* (pluralized) */ | ||
| 297 | "yr": RrdTime.YEARS, /* (generic) */ | ||
| 298 | "y": RrdTime.YEARS /* (short generic) */ | ||
| 299 | }; | ||
| 300 | |||
| 301 | RrdTime.ABSOLUTE_TIME = 0; | ||
| 302 | RrdTime.RELATIVE_TO_START_TIME = 1; | ||
| 303 | RrdTime.RELATIVE_TO_END_TIME = 2; | ||
| 304 | RrdTime.RELATIVE_TO_EPOCH = 3; | ||
| 305 | |||
| 306 | RrdTime.prototype.gettok = function () | ||
| 307 | { | ||
| 308 | if (this.tokidx >= this.toklen) { | ||
| 309 | this.tokid = RrdTime.EOF; | ||
| 310 | } else { | ||
| 311 | this.token = this.tokens[this.tokidx]; | ||
| 312 | this.tokidx++; | ||
| 313 | if (!isNaN(this.token)) { | ||
| 314 | this.tokid = RrdTime.NUMBER; | ||
| 315 | this.token = parseInt(this.token, 10); | ||
| 316 | } else if (this.token === ':') { | ||
| 317 | this.tokid = RrdTime.COLON; | ||
| 318 | } else if (this.token === '.') { | ||
| 319 | this.tokid = RrdTime.DOT; | ||
| 320 | } else if (this.token === '+') { | ||
| 321 | this.tokid = RrdTime.PLUS; | ||
| 322 | } else if (this.token === '/') { | ||
| 323 | this.tokid = RrdTime.SLASH; | ||
| 324 | } else if (this.token === '-') { | ||
| 325 | this.tokid = RrdTime.MINUS; | ||
| 326 | } else { | ||
| 327 | this.tokid = RrdTime.ID; | ||
| 328 | if (this.token in this.specials) | ||
| 329 | this.tokid = this.specials[this.token]; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | return this.tokid; | ||
| 333 | }; | ||
| 334 | |||
| 335 | RrdTime.prototype.plus_minus = function (doop) | ||
| 336 | { | ||
| 337 | var op = RrdTime.PLUS; | ||
| 338 | var prev_multiplier = -1; | ||
| 339 | var delta; | ||
| 340 | |||
| 341 | if (doop >= 0) { | ||
| 342 | op = doop; | ||
| 343 | if (this.gettok() != RrdTime.NUMBER) | ||
| 344 | throw new RrdTimeError("There should be number after '"+(op == RrdTime.PLUS ? '+' : '-')+"'"); | ||
| 345 | prev_multiplier = -1; /* reset months-minutes guessing mechanics */ | ||
| 346 | } | ||
| 347 | /* if doop is < 0 then we repeat the previous op with the prefetched number */ | ||
| 348 | delta = this.token; | ||
| 349 | if (this.gettok() == RrdTime.MONTHS_MINUTES) { | ||
| 350 | /* hard job to guess what does that -5m means: -5mon or -5min? */ | ||
| 351 | switch (prev_multiplier) { | ||
| 352 | case RrdTime.DAYS: | ||
| 353 | case RrdTime.WEEKS: | ||
| 354 | case RrdTime.MONTHS: | ||
| 355 | case RrdTime.YEARS: | ||
| 356 | this.tokid = RrdTime.MONTHS; | ||
| 357 | break; | ||
| 358 | case RrdTime.SECONDS: | ||
| 359 | case RrdTime.MINUTES: | ||
| 360 | case RrdTime.HOURS: | ||
| 361 | this.tokid = RrdTime.MINUTES; | ||
| 362 | break; | ||
| 363 | default: | ||
| 364 | if (delta < 6) /* it may be some other value but in the context of RRD who needs less than 6 min deltas? */ | ||
| 365 | this.tokid = RrdTime.MONTHS; | ||
| 366 | else | ||
| 367 | this.tokid = RrdTime.MINUTES; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | prev_multiplier = this.tokid; | ||
| 371 | switch (this.tokid) { | ||
| 372 | case RrdTime.YEARS: | ||
| 373 | this.tm_year += ( op == RrdTime.PLUS) ? delta : -delta; | ||
| 374 | return; | ||
| 375 | case RrdTime.MONTHS: | ||
| 376 | this.tm_mon += ( op == RrdTime.PLUS) ? delta : -delta; | ||
| 377 | return; | ||
| 378 | case RrdTime.WEEKS: | ||
| 379 | delta *= 7; | ||
| 380 | case RrdTime.DAYS: | ||
| 381 | this.tm_mday += ( op == RrdTime.PLUS) ? delta : -delta; | ||
| 382 | return; | ||
| 383 | case RrdTime.HOURS: | ||
| 384 | this.offset += (op == RrdTime.PLUS) ? delta * 60 * 60 : -delta * 60 * 60; | ||
| 385 | return; | ||
| 386 | case RrdTime.MINUTES: | ||
| 387 | this.offset += (op == RrdTime.PLUS) ? delta * 60 : -delta * 60; | ||
| 388 | return; | ||
| 389 | case RrdTime.SECONDS: | ||
| 390 | this.offset += (op == RrdTime.PLUS) ? delta : -delta; | ||
| 391 | return; | ||
| 392 | default: /*default unit is seconds */ | ||
| 393 | this.offset += (op == RrdTime.PLUS) ? delta : -delta; | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | throw new RrdTimeError("well-known time unit expected after "+delta); | ||
| 397 | }; | ||
| 398 | |||
| 399 | RrdTime.prototype.tod = function() /* tod() computes the time of day (TIME-OF-DAY-SPEC) */ | ||
| 400 | { | ||
| 401 | var hour, minute = 0; | ||
| 402 | var tlen; | ||
| 403 | /* save token status in case we must abort */ | ||
| 404 | var tokid_sv = this.tokid; | ||
| 405 | |||
| 406 | tlen = (this.token+"").length; | ||
| 407 | /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */ | ||
| 408 | if (tlen > 2) | ||
| 409 | return; | ||
| 410 | hour = this.token; | ||
| 411 | this.gettok(); | ||
| 412 | if (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT) { | ||
| 413 | /* guess we are looking at a date */ | ||
| 414 | this.tokid = tokid_sv; | ||
| 415 | this.token = hour; | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | if (this.tokid == RrdTime.COLON) { | ||
| 419 | if (this.gettok() != RrdTime.NUMBER) | ||
| 420 | throw new RrdTimeError("Parsing HH:MM syntax, expecting MM as number, got none"); | ||
| 421 | minute = this.token; | ||
| 422 | if (minute > 59) | ||
| 423 | throw new RrdTimeError("parsing HH:MM syntax, got MM = "+minute+" (>59!)"); | ||
| 424 | this.gettok(); | ||
| 425 | } | ||
| 426 | /* check if an AM or PM specifier was given */ | ||
| 427 | if (this.tokid == RrdTime.AM || this.tokid == RrdTime.PM) { | ||
| 428 | if (hour > 12) { | ||
| 429 | throw new RrdTimeError("there cannot be more than 12 AM or PM hours"); | ||
| 430 | } | ||
| 431 | if (this.tokid == RrdTime.PM) { | ||
| 432 | if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ | ||
| 433 | hour += 12; | ||
| 434 | } else { | ||
| 435 | if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ | ||
| 436 | hour = 0; | ||
| 437 | } | ||
| 438 | this.gettok(); | ||
| 439 | } else if (hour > 23) { | ||
| 440 | /* guess it was not a time then ... */ | ||
| 441 | this.tokid = tokid_sv; | ||
| 442 | this.token = hour; | ||
| 443 | return; | ||
| 444 | } | ||
| 445 | this.tm_hour = hour; | ||
| 446 | this.tm_min = minute; | ||
| 447 | this.tm_sec = 0; | ||
| 448 | if (this.tm_hour == 24) { | ||
| 449 | this.tm_hour = 0; | ||
| 450 | this.tm_mday++; | ||
| 451 | } | ||
| 452 | }; | ||
| 453 | |||
| 454 | RrdTime.prototype.assign_date = function(mday, mon, year) | ||
| 455 | { | ||
| 456 | if (year > 138) { | ||
| 457 | if (year > 1970) { | ||
| 458 | year -= 1900; | ||
| 459 | } else { | ||
| 460 | throw new RrdTimeError("invalid year "+year+" (should be either 00-99 or >1900)"); | ||
| 461 | } | ||
| 462 | } else if (year >= 0 && year < 38) { | ||
| 463 | year += 100; /* Allow year 2000-2037 to be specified as */ | ||
| 464 | } | ||
| 465 | /* 00-37 until the problem of 2038 year will */ | ||
| 466 | /* arise for unices with 32-bit time_t :) */ | ||
| 467 | if (year < 70) | ||
| 468 | throw new RrdTimeError("won't handle dates before epoch (01/01/1970), sorry"); | ||
| 469 | |||
| 470 | this.tm_mday = mday; | ||
| 471 | this.tm_mon = mon; | ||
| 472 | this.tm_year = year; | ||
| 473 | }; | ||
| 474 | |||
| 475 | RrdTime.prototype.day = function () | ||
| 476 | { | ||
| 477 | var mday = 0, wday, mon, year = this.tm_year; | ||
| 478 | var tlen; | ||
| 479 | |||
| 480 | switch (this.tokid) { | ||
| 481 | case RrdTime.YESTERDAY: | ||
| 482 | this.tm_mday--; | ||
| 483 | case RrdTime.TODAY: | ||
| 484 | this.gettok(); | ||
| 485 | break; | ||
| 486 | case RrdTime.TOMORROW: | ||
| 487 | this.tm_mday++; | ||
| 488 | this.gettok(); | ||
| 489 | break; | ||
| 490 | case RrdTime.JAN: | ||
| 491 | case RrdTime.FEB: | ||
| 492 | case RrdTime.MAR: | ||
| 493 | case RrdTime.APR: | ||
| 494 | case RrdTime.MAY: | ||
| 495 | case RrdTime.JUN: | ||
| 496 | case RrdTime.JUL: | ||
| 497 | case RrdTime.AUG: | ||
| 498 | case RrdTime.SEP: | ||
| 499 | case RrdTime.OCT: | ||
| 500 | case RrdTime.NOV: | ||
| 501 | case RrdTime.DEC: | ||
| 502 | mon = (this.tokid - RrdTime.JAN); | ||
| 503 | if (this.gettok() != RrdTime.NUMBER) | ||
| 504 | throw new RrdTimeError("the day of the month should follow month name"); | ||
| 505 | mday = this.token; | ||
| 506 | if (this.gettok() == RrdTime.NUMBER) { | ||
| 507 | year = this.token; | ||
| 508 | this.gettok(); | ||
| 509 | } else { | ||
| 510 | year = this.tm_year; | ||
| 511 | } | ||
| 512 | this.assign_date(mday, mon, year); | ||
| 513 | break; | ||
| 514 | case RrdTime.SUN: | ||
| 515 | case RrdTime.MON: | ||
| 516 | case RrdTime.TUE: | ||
| 517 | case RrdTime.WED: | ||
| 518 | case RrdTime.THU: | ||
| 519 | case RrdTime.FRI: | ||
| 520 | case RrdTime.SAT: | ||
| 521 | wday = (this.tokid - RrdTime.SUN); | ||
| 522 | this.tm_mday += (wday - this.tm_wday); | ||
| 523 | this.gettok(); | ||
| 524 | break; | ||
| 525 | case RrdTime.NUMBER: | ||
| 526 | mon = this.token; | ||
| 527 | if (mon > 10 * 365 * 24 * 60 * 60) { | ||
| 528 | this.localtime(mon); | ||
| 529 | this.gettok(); | ||
| 530 | break; | ||
| 531 | } | ||
| 532 | if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */ | ||
| 533 | var str = this.token + ''; | ||
| 534 | year = parseInt(str.substr(0,4),10); | ||
| 535 | mon = parseInt(str.substr(4,2),10); | ||
| 536 | mday = parseInt(str.substr(6,2),10); | ||
| 537 | this.gettok(); | ||
| 538 | } else { | ||
| 539 | this.gettok(); | ||
| 540 | if (mon <= 31 && (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT)) { | ||
| 541 | var sep = this.tokid; | ||
| 542 | if (this.gettok() != RrdTime.NUMBER) | ||
| 543 | throw new RrdTimeError("there should be "+(RrdTime.DOT ? "month" : "day")+" number after '"+(RrdTime.DOT ? '.' : '/')+"'"); | ||
| 544 | mday = this.token; | ||
| 545 | if (this.gettok() == sep) { | ||
| 546 | if (this.gettok() != RrdTime.NUMBER) | ||
| 547 | throw new RrdTimeError("there should be year number after '"+(sep == RrdTime.DOT ? '.' : '/')+"'"); | ||
| 548 | year = this.token; | ||
| 549 | this.gettok(); | ||
| 550 | } | ||
| 551 | if (sep == RrdTime.DOT) { | ||
| 552 | var x = mday; | ||
| 553 | mday = mon; | ||
| 554 | mon = x; | ||
| 555 | } | ||
| 556 | } | ||
| 557 | } | ||
| 558 | mon--; | ||
| 559 | if (mon < 0 || mon > 11) | ||
| 560 | throw new RrdTimeError("did you really mean month "+(mon+1)+"?"); | ||
| 561 | if (mday < 1 || mday > 31) | ||
| 562 | throw new RrdTimeError("I'm afraid that "+mday+" is not a valid day of the month"); | ||
| 563 | this.assign_date(mday, mon, year); | ||
| 564 | break; | ||
| 565 | } | ||
| 566 | }; | ||
| 567 | |||
| 568 | RrdTime.prototype.localtime = function (tm) | ||
| 569 | { | ||
| 570 | var date = new Date(tm*1000); | ||
| 571 | this.tm_sec = date.getSeconds(); | ||
| 572 | this.tm_min = date.getMinutes(); | ||
| 573 | this.tm_hour = date.getHours(); | ||
| 574 | this.tm_mday = date.getDate(); | ||
| 575 | this.tm_mon = date.getMonth(); | ||
| 576 | this.tm_year = date.getFullYear()-1900; | ||
| 577 | this.tm_wday = date.getDay(); | ||
| 578 | }; | ||
| 579 | |||
| 580 | RrdTime.prototype.mktime = function() | ||
| 581 | { | ||
| 582 | var date = new Date(this.tm_year+1900, this.tm_mon, this.tm_mday, this.tm_hour, this.tm_min, this.tm_sec); | ||
| 583 | return Math.round(date.getTime()/1000.0); | ||
| 584 | }; | ||
| 585 | |||
| 586 | RrdTime.proc_start_end = function(start_t, end_t) | ||
| 587 | { | ||
| 588 | var start, end; | ||
| 589 | |||
| 590 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME && end_t.type == RrdTime.RELATIVE_TO_START_TIME) | ||
| 591 | throw new RrdTimeError("the start and end times cannot be specified relative to each other"); | ||
| 592 | if (start_t.type == RrdTime.RELATIVE_TO_START_TIME) | ||
| 593 | throw new RrdTimeError("the start time cannot be specified relative to itself"); | ||
| 594 | if (end_t.type == RrdTime.RELATIVE_TO_END_TIME) | ||
| 595 | throw new RrdTimeError("the end time cannot be specified relative to itself"); | ||
| 596 | |||
| 597 | if (start_t.type == RrdTime.RELATIVE_TO_END_TIME) { | ||
| 598 | end = end_t.mktime() + end_t.offset; | ||
| 599 | var tmtmp = new Date(end*1000); | ||
| 600 | tmtmp.setDate(tmtmp.getDate()+start_t.tm_mday); | ||
| 601 | tmtmp.setMonth(tmtmp.getMonth()+start_t.tm_mon); | ||
| 602 | tmtmp.setFullYear(tmtmp.getFullYear()+start_t.tm_year); | ||
| 603 | start = Math.round(tmtmp.getTime()/1000.0) + start_t.offset; | ||
| 604 | } else { | ||
| 605 | start = start_t.mktime() + start_t.offset; | ||
| 606 | } | ||
| 607 | |||
| 608 | if (end_t.type == RrdTime.RELATIVE_TO_START_TIME) { | ||
| 609 | start = start_t.mktime() + start_t.offset; | ||
| 610 | var tmtmp = new Date(start*1000); | ||
| 611 | tmtmp.setDate(tmtmp.getDate()+end_t.tm_mday); | ||
| 612 | tmtmp.setMonth(tmtmp.getMonth()+end_t.tm_mon); | ||
| 613 | tmtmp.setFullYear(tmtmp.getFullYear()+end_t.tm_year); | ||
| 614 | end = Math.round(tmtmp.getTime()/1000.0) + end_t.offset; | ||
| 615 | } else { | ||
| 616 | end = end_t.mktime() + end_t.offset; | ||
| 617 | } | ||
| 618 | |||
| 619 | return [start, end]; | ||
| 620 | }; | ||
| 621 | |||
diff --git a/js/base64.js b/js/base64.js new file mode 100644 index 0000000..7d9536a --- /dev/null +++ b/js/base64.js | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | |||
| 2 | /** | ||
| 3 | * | ||
| 4 | * Base64 encode / decode | ||
| 5 | * http://www.webtoolkit.info/ | ||
| 6 | * | ||
| 7 | **/ | ||
| 8 | |||
| 9 | var Base64 = { | ||
| 10 | |||
| 11 | // private property | ||
| 12 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", | ||
| 13 | |||
| 14 | // public method for encoding | ||
| 15 | encode : function (input) { | ||
| 16 | var output = ""; | ||
| 17 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; | ||
| 18 | var i = 0; | ||
| 19 | |||
| 20 | input = Base64._utf8_encode(input); | ||
| 21 | |||
| 22 | while (i < input.length) { | ||
| 23 | |||
| 24 | chr1 = input.charCodeAt(i++); | ||
| 25 | chr2 = input.charCodeAt(i++); | ||
| 26 | chr3 = input.charCodeAt(i++); | ||
| 27 | |||
| 28 | enc1 = chr1 >> 2; | ||
| 29 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | ||
| 30 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | ||
| 31 | enc4 = chr3 & 63; | ||
| 32 | |||
| 33 | if (isNaN(chr2)) { | ||
| 34 | enc3 = enc4 = 64; | ||
| 35 | } else if (isNaN(chr3)) { | ||
| 36 | enc4 = 64; | ||
| 37 | } | ||
| 38 | |||
| 39 | output = output + | ||
| 40 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + | ||
| 41 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); | ||
| 42 | |||
| 43 | } | ||
| 44 | |||
| 45 | return output; | ||
| 46 | }, | ||
| 47 | |||
| 48 | // public method for decoding | ||
| 49 | decode : function (input) { | ||
| 50 | var output = ""; | ||
| 51 | var chr1, chr2, chr3; | ||
| 52 | var enc1, enc2, enc3, enc4; | ||
| 53 | var i = 0; | ||
| 54 | |||
| 55 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | ||
| 56 | |||
| 57 | while (i < input.length) { | ||
| 58 | |||
| 59 | enc1 = this._keyStr.indexOf(input.charAt(i++)); | ||
| 60 | enc2 = this._keyStr.indexOf(input.charAt(i++)); | ||
| 61 | enc3 = this._keyStr.indexOf(input.charAt(i++)); | ||
| 62 | enc4 = this._keyStr.indexOf(input.charAt(i++)); | ||
| 63 | |||
| 64 | chr1 = (enc1 << 2) | (enc2 >> 4); | ||
| 65 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | ||
| 66 | chr3 = ((enc3 & 3) << 6) | enc4; | ||
| 67 | |||
| 68 | output = output + String.fromCharCode(chr1); | ||
| 69 | |||
| 70 | if (enc3 != 64) { | ||
| 71 | output = output + String.fromCharCode(chr2); | ||
| 72 | } | ||
| 73 | if (enc4 != 64) { | ||
| 74 | output = output + String.fromCharCode(chr3); | ||
| 75 | } | ||
| 76 | |||
| 77 | } | ||
| 78 | |||
| 79 | output = Base64._utf8_decode(output); | ||
| 80 | |||
| 81 | return output; | ||
| 82 | |||
| 83 | }, | ||
| 84 | |||
| 85 | // private method for UTF-8 encoding | ||
| 86 | _utf8_encode : function (string) { | ||
| 87 | string = string.replace(/\r\n/g,"\n"); | ||
| 88 | var utftext = ""; | ||
| 89 | |||
| 90 | for (var n = 0; n < string.length; n++) { | ||
| 91 | |||
| 92 | var c = string.charCodeAt(n); | ||
| 93 | |||
| 94 | if (c < 128) { | ||
| 95 | utftext += String.fromCharCode(c); | ||
| 96 | } | ||
| 97 | else if((c > 127) && (c < 2048)) { | ||
| 98 | utftext += String.fromCharCode((c >> 6) | 192); | ||
| 99 | utftext += String.fromCharCode((c & 63) | 128); | ||
| 100 | } | ||
| 101 | else { | ||
| 102 | utftext += String.fromCharCode((c >> 12) | 224); | ||
| 103 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); | ||
| 104 | utftext += String.fromCharCode((c & 63) | 128); | ||
| 105 | } | ||
| 106 | |||
| 107 | } | ||
| 108 | |||
| 109 | return utftext; | ||
| 110 | }, | ||
| 111 | |||
| 112 | // private method for UTF-8 decoding | ||
| 113 | _utf8_decode : function (utftext) { | ||
| 114 | var string = ""; | ||
| 115 | var i = 0; | ||
| 116 | var c = c1 = c2 = 0; | ||
| 117 | |||
| 118 | while ( i < utftext.length ) { | ||
| 119 | |||
| 120 | c = utftext.charCodeAt(i); | ||
| 121 | |||
| 122 | if (c < 128) { | ||
| 123 | string += String.fromCharCode(c); | ||
| 124 | i++; | ||
| 125 | } | ||
| 126 | else if((c > 191) && (c < 224)) { | ||
| 127 | c2 = utftext.charCodeAt(i+1); | ||
| 128 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); | ||
| 129 | i += 2; | ||
| 130 | } | ||
| 131 | else { | ||
| 132 | c2 = utftext.charCodeAt(i+1); | ||
| 133 | c3 = utftext.charCodeAt(i+2); | ||
| 134 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); | ||
| 135 | i += 3; | ||
| 136 | } | ||
| 137 | |||
| 138 | } | ||
| 139 | |||
| 140 | return string; | ||
| 141 | } | ||
| 142 | |||
| 143 | } | ||
diff --git a/js/binaryXHR.js b/js/binaryXHR.js new file mode 100644 index 0000000..d91ad72 --- /dev/null +++ b/js/binaryXHR.js | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | |||
| 2 | /* | ||
| 3 | * BinaryFile over XMLHttpRequest | ||
| 4 | * Part of the javascriptRRD package | ||
| 5 | * Copyright (c) 2009 Frank Wuerthwein, fkw@ucsd.edu | ||
| 6 | * MIT License [http://www.opensource.org/licenses/mit-license.php] | ||
| 7 | * | ||
| 8 | * Original repository: http://javascriptrrd.sourceforge.net/ | ||
| 9 | * | ||
| 10 | * Based on: | ||
| 11 | * Binary Ajax 0.1.5 | ||
| 12 | * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ | ||
| 13 | * MIT License [http://www.opensource.org/licenses/mit-license.php] | ||
| 14 | */ | ||
| 15 | |||
| 16 | // ============================================================ | ||
| 17 | // Exception class | ||
| 18 | function InvalidBinaryFile(msg) { | ||
| 19 | this.message=msg; | ||
| 20 | this.name="Invalid BinaryFile"; | ||
| 21 | } | ||
| 22 | |||
| 23 | // pretty print | ||
| 24 | InvalidBinaryFile.prototype.toString = function() { | ||
| 25 | return this.name + ': "' + this.message + '"'; | ||
| 26 | } | ||
| 27 | |||
| 28 | // ===================================================================== | ||
| 29 | // BinaryFile class | ||
| 30 | // Allows access to element inside a binary stream | ||
| 31 | function BinaryFile(strData, iDataOffset, iDataLength) { | ||
| 32 | var data = strData; | ||
| 33 | var dataOffset = iDataOffset || 0; | ||
| 34 | var dataLength = 0; | ||
| 35 | // added | ||
| 36 | var doubleMantExpHi=Math.pow(2,-28); | ||
| 37 | var doubleMantExpLo=Math.pow(2,-52); | ||
| 38 | var doubleMantExpFast=Math.pow(2,-20); | ||
| 39 | |||
| 40 | this.getRawData = function() { | ||
| 41 | return data; | ||
| 42 | } | ||
| 43 | |||
| 44 | if (typeof strData == "string") { | ||
| 45 | dataLength = iDataLength || data.length; | ||
| 46 | |||
| 47 | this.getByteAt = function(iOffset) { | ||
| 48 | return data.charCodeAt(iOffset + dataOffset) & 0xFF; | ||
| 49 | } | ||
| 50 | } else if (typeof strData == "unknown") { | ||
| 51 | dataLength = iDataLength || IEBinary_getLength(data); | ||
| 52 | |||
| 53 | this.getByteAt = function(iOffset) { | ||
| 54 | return IEBinary_getByteAt(data, iOffset + dataOffset); | ||
| 55 | } | ||
| 56 | } else { | ||
| 57 | throw new InvalidBinaryFile("Unsupported type " + (typeof strData)); | ||
| 58 | } | ||
| 59 | |||
| 60 | this.getLength = function() { | ||
| 61 | return dataLength; | ||
| 62 | } | ||
| 63 | |||
| 64 | this.getSByteAt = function(iOffset) { | ||
| 65 | var iByte = this.getByteAt(iOffset); | ||
| 66 | if (iByte > 127) | ||
| 67 | return iByte - 256; | ||
| 68 | else | ||
| 69 | return iByte; | ||
| 70 | } | ||
| 71 | |||
| 72 | this.getShortAt = function(iOffset) { | ||
| 73 | var iShort = (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) | ||
| 74 | if (iShort < 0) iShort += 65536; | ||
| 75 | return iShort; | ||
| 76 | } | ||
| 77 | this.getSShortAt = function(iOffset) { | ||
| 78 | var iUShort = this.getShortAt(iOffset); | ||
| 79 | if (iUShort > 32767) | ||
| 80 | return iUShort - 65536; | ||
| 81 | else | ||
| 82 | return iUShort; | ||
| 83 | } | ||
| 84 | this.getLongAt = function(iOffset) { | ||
| 85 | var iByte1 = this.getByteAt(iOffset), | ||
| 86 | iByte2 = this.getByteAt(iOffset + 1), | ||
| 87 | iByte3 = this.getByteAt(iOffset + 2), | ||
| 88 | iByte4 = this.getByteAt(iOffset + 3); | ||
| 89 | |||
| 90 | var iLong = (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; | ||
| 91 | if (iLong < 0) iLong += 4294967296; | ||
| 92 | return iLong; | ||
| 93 | } | ||
| 94 | this.getSLongAt = function(iOffset) { | ||
| 95 | var iULong = this.getLongAt(iOffset); | ||
| 96 | if (iULong > 2147483647) | ||
| 97 | return iULong - 4294967296; | ||
| 98 | else | ||
| 99 | return iULong; | ||
| 100 | } | ||
| 101 | this.getStringAt = function(iOffset, iLength) { | ||
| 102 | var aStr = []; | ||
| 103 | for (var i=iOffset,j=0;i<iOffset+iLength;i++,j++) { | ||
| 104 | aStr[j] = String.fromCharCode(this.getByteAt(i)); | ||
| 105 | } | ||
| 106 | return aStr.join(""); | ||
| 107 | } | ||
| 108 | |||
| 109 | // Added | ||
| 110 | this.getCStringAt = function(iOffset, iMaxLength) { | ||
| 111 | var aStr = []; | ||
| 112 | for (var i=iOffset,j=0;(i<iOffset+iMaxLength) && (this.getByteAt(i)>0);i++,j++) { | ||
| 113 | aStr[j] = String.fromCharCode(this.getByteAt(i)); | ||
| 114 | } | ||
| 115 | return aStr.join(""); | ||
| 116 | } | ||
| 117 | |||
| 118 | // Added | ||
| 119 | this.getDoubleAt = function(iOffset) { | ||
| 120 | var iByte1 = this.getByteAt(iOffset), | ||
| 121 | iByte2 = this.getByteAt(iOffset + 1), | ||
| 122 | iByte3 = this.getByteAt(iOffset + 2), | ||
| 123 | iByte4 = this.getByteAt(iOffset + 3), | ||
| 124 | iByte5 = this.getByteAt(iOffset + 4), | ||
| 125 | iByte6 = this.getByteAt(iOffset + 5), | ||
| 126 | iByte7 = this.getByteAt(iOffset + 6), | ||
| 127 | iByte8 = this.getByteAt(iOffset + 7); | ||
| 128 | var iSign=iByte8 >> 7; | ||
| 129 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); | ||
| 130 | var iMantHi=((((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5) << 8) + iByte4; | ||
| 131 | var iMantLo=((((iByte3) << 8) + iByte2) << 8) + iByte1; | ||
| 132 | |||
| 133 | if (iExpRaw==0) return 0.0; | ||
| 134 | if (iExpRaw==0x7ff) return undefined; | ||
| 135 | |||
| 136 | var iExp=(iExpRaw & 0x7FF)-1023; | ||
| 137 | |||
| 138 | var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMantLo*doubleMantExpLo + iMantHi*doubleMantExpHi); | ||
| 139 | return dDouble; | ||
| 140 | } | ||
| 141 | // added | ||
| 142 | // Extracts only 4 bytes out of 8, loosing in precision (20 bit mantissa) | ||
| 143 | this.getFastDoubleAt = function(iOffset) { | ||
| 144 | var iByte5 = this.getByteAt(iOffset + 4), | ||
| 145 | iByte6 = this.getByteAt(iOffset + 5), | ||
| 146 | iByte7 = this.getByteAt(iOffset + 6), | ||
| 147 | iByte8 = this.getByteAt(iOffset + 7); | ||
| 148 | var iSign=iByte8 >> 7; | ||
| 149 | var iExpRaw=((iByte8 & 0x7F)<< 4) + (iByte7 >> 4); | ||
| 150 | var iMant=((((iByte7 & 0x0F) << 8) + iByte6) << 8) + iByte5; | ||
| 151 | |||
| 152 | if (iExpRaw==0) return 0.0; | ||
| 153 | if (iExpRaw==0x7ff) return undefined; | ||
| 154 | |||
| 155 | var iExp=(iExpRaw & 0x7FF)-1023; | ||
| 156 | |||
| 157 | var dDouble = ((iSign==1)?-1:1)*Math.pow(2,iExp)*(1.0 + iMant*doubleMantExpFast); | ||
| 158 | return dDouble; | ||
| 159 | } | ||
| 160 | |||
| 161 | this.getCharAt = function(iOffset) { | ||
| 162 | return String.fromCharCode(this.getByteAt(iOffset)); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 167 | document.write( | ||
| 168 | "<script type='text/vbscript'>\r\n" | ||
| 169 | + "Function IEBinary_getByteAt(strBinary, iOffset)\r\n" | ||
| 170 | + " IEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\n" | ||
| 171 | + "End Function\r\n" | ||
| 172 | + "Function IEBinary_getLength(strBinary)\r\n" | ||
| 173 | + " IEBinary_getLength = LenB(strBinary)\r\n" | ||
| 174 | + "End Function\r\n" | ||
| 175 | + "</script>\r\n" | ||
| 176 | ); | ||
| 177 | |||
| 178 | |||
| 179 | |||
| 180 | // =============================================================== | ||
| 181 | // Load a binary file from the specified URL | ||
| 182 | // Will return an object of type BinaryFile | ||
| 183 | function FetchBinaryURL(url) { | ||
| 184 | var request = new XMLHttpRequest(); | ||
| 185 | request.open("GET", url,false); | ||
| 186 | try { | ||
| 187 | request.overrideMimeType('text/plain; charset=x-user-defined'); | ||
| 188 | } catch (err) { | ||
| 189 | // ignore any error, just to make both FF and IE work | ||
| 190 | } | ||
| 191 | request.send(null); | ||
| 192 | |||
| 193 | var response=request.responseBody; | ||
| 194 | if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE | ||
| 195 | response=request.responseText; | ||
| 196 | } | ||
| 197 | var bf=new BinaryFile(response); | ||
| 198 | return bf; | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 202 | // =============================================================== | ||
| 203 | // Asyncronously load a binary file from the specified URL | ||
| 204 | // | ||
| 205 | // callback must be a function with one or two arguments: | ||
| 206 | // - bf = an object of type BinaryFile | ||
| 207 | // - optional argument object (used only if callback_arg not undefined) | ||
| 208 | function FetchBinaryURLAsync(url, callback, callback_arg) { | ||
| 209 | var callback_wrapper = function() { | ||
| 210 | if(this.readyState == 4) { | ||
| 211 | var response=this.responseBody; | ||
| 212 | if (response==undefined){ // responseBody is non standard, but the only way to make it work in IE | ||
| 213 | response=this.responseText; | ||
| 214 | } | ||
| 215 | var bf=new BinaryFile(response); | ||
| 216 | if (callback_arg!=null) { | ||
| 217 | callback(bf,callback_arg); | ||
| 218 | } else { | ||
| 219 | callback(bf); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | var request = new XMLHttpRequest(); | ||
| 225 | request.onreadystatechange = callback_wrapper; | ||
| 226 | request.open("GET", url,true); | ||
| 227 | try { | ||
| 228 | request.overrideMimeType('text/plain; charset=x-user-defined'); | ||
| 229 | } catch (err) { | ||
| 230 | // ignore any error, just to make both FF and IE work | ||
| 231 | } | ||
| 232 | request.send(null); | ||
| 233 | return request | ||
| 234 | } | ||
diff --git a/js/rrdFile.js b/js/rrdFile.js new file mode 100644 index 0000000..870c88e --- /dev/null +++ b/js/rrdFile.js | |||
| @@ -0,0 +1,408 @@ | |||
| 1 | /* | ||
| 2 | * Client library for access to RRD archive files | ||
| 3 | * Part of the javascriptRRD package | ||
| 4 | * Copyright (c) 2009-2010 Frank Wuerthwein, fkw@ucsd.edu | ||
| 5 | * Igor Sfiligoi, isfiligoi@ucsd.edu | ||
| 6 | * | ||
| 7 | * Original repository: http://javascriptrrd.sourceforge.net/ | ||
| 8 | * | ||
| 9 | * MIT License [http://www.opensource.org/licenses/mit-license.php] | ||
| 10 | * | ||
| 11 | */ | ||
| 12 | |||
| 13 | /* | ||
| 14 | * | ||
| 15 | * RRDTool has been developed and is maintained by | ||
| 16 | * Tobias Oether [http://oss.oetiker.ch/rrdtool/] | ||
| 17 | * | ||
| 18 | * This software can be used to read files produced by the RRDTool | ||
| 19 | * but has been developed independently. | ||
| 20 | * | ||
| 21 | * Limitations: | ||
| 22 | * | ||
| 23 | * This version of the module assumes RRD files created on linux | ||
| 24 | * with intel architecture and supports both 32 and 64 bit CPUs. | ||
| 25 | * All integers in RRD files are suppoes to fit in 32bit values. | ||
| 26 | * | ||
| 27 | * Only versions 3 and 4 of the RRD archive are supported. | ||
| 28 | * | ||
| 29 | * Only AVERAGE,MAXIMUM,MINIMUM and LAST consolidation functions are | ||
| 30 | * supported. For all others, the behaviour is at the moment undefined. | ||
| 31 | * | ||
| 32 | */ | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Dependencies: | ||
| 36 | * | ||
| 37 | * The data provided to this module require an object of a class | ||
| 38 | * that implements the following methods: | ||
| 39 | * getByteAt(idx) - Return a 8 bit unsigned integer at offset idx | ||
| 40 | * getLongAt(idx) - Return a 32 bit unsigned integer at offset idx | ||
| 41 | * getDoubleAt(idx) - Return a double float at offset idx | ||
| 42 | * getFastDoubleAt(idx) - Similar to getDoubleAt but with less precision | ||
| 43 | * getCStringAt(idx,maxsize) - Return a string of at most maxsize characters | ||
| 44 | * that was 0-terminated in the source | ||
| 45 | * | ||
| 46 | * The BinaryFile from binaryXHR.js implements this interface. | ||
| 47 | * | ||
| 48 | */ | ||
| 49 | |||
| 50 | |||
| 51 | // ============================================================ | ||
| 52 | // Exception class | ||
| 53 | function InvalidRRD(msg) { | ||
| 54 | this.message=msg; | ||
| 55 | this.name="Invalid RRD"; | ||
| 56 | } | ||
| 57 | |||
| 58 | // pretty print | ||
| 59 | InvalidRRD.prototype.toString = function() { | ||
| 60 | return this.name + ': "' + this.message + '"'; | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | // ============================================================ | ||
| 65 | // RRD DS Info class | ||
| 66 | function RRDDS(rrd_data,rrd_data_idx,my_idx) { | ||
| 67 | this.rrd_data=rrd_data; | ||
| 68 | this.rrd_data_idx=rrd_data_idx; | ||
| 69 | this.my_idx=my_idx; | ||
| 70 | } | ||
| 71 | |||
| 72 | RRDDS.prototype.getIdx = function() { | ||
| 73 | return this.my_idx; | ||
| 74 | } | ||
| 75 | RRDDS.prototype.getName = function() { | ||
| 76 | return this.rrd_data.getCStringAt(this.rrd_data_idx,20); | ||
| 77 | } | ||
| 78 | RRDDS.prototype.getType = function() { | ||
| 79 | return this.rrd_data.getCStringAt(this.rrd_data_idx+20,20); | ||
| 80 | } | ||
| 81 | RRDDS.prototype.getMin = function() { | ||
| 82 | return this.rrd_data.getDoubleAt(this.rrd_data_idx+48); | ||
| 83 | } | ||
| 84 | RRDDS.prototype.getMax = function() { | ||
| 85 | return this.rrd_data.getDoubleAt(this.rrd_data_idx+56); | ||
| 86 | } | ||
| 87 | |||
| 88 | |||
| 89 | // ============================================================ | ||
| 90 | // RRD RRA Info class | ||
| 91 | function RRDRRAInfo(rrd_data,rra_def_idx, | ||
| 92 | rrd_align,row_cnt,pdp_step,my_idx) { | ||
| 93 | this.rrd_data=rrd_data; | ||
| 94 | this.rra_def_idx=rra_def_idx; | ||
| 95 | this.rrd_align=rrd_align; | ||
| 96 | this.row_cnt=row_cnt; | ||
| 97 | this.pdp_step=pdp_step; | ||
| 98 | this.my_idx=my_idx; | ||
| 99 | } | ||
| 100 | |||
| 101 | RRDRRAInfo.prototype.getIdx = function() { | ||
| 102 | return this.my_idx; | ||
| 103 | } | ||
| 104 | |||
| 105 | // Get number of rows | ||
| 106 | RRDRRAInfo.prototype.getNrRows = function() { | ||
| 107 | return this.row_cnt; | ||
| 108 | } | ||
| 109 | |||
| 110 | // Get number of slots used for consolidation | ||
| 111 | // Mostly for internal use | ||
| 112 | RRDRRAInfo.prototype.getPdpPerRow = function() { | ||
| 113 | if (this.rrd_align==32) | ||
| 114 | return this.rrd_data.getLongAt(this.rra_def_idx+24,20); | ||
| 115 | else | ||
| 116 | return this.rrd_data.getLongAt(this.rra_def_idx+32,20); | ||
| 117 | } | ||
| 118 | |||
| 119 | // Get RRA step (expressed in seconds) | ||
| 120 | RRDRRAInfo.prototype.getStep = function() { | ||
| 121 | return this.pdp_step*this.getPdpPerRow(); | ||
| 122 | } | ||
| 123 | |||
| 124 | // Get consolidation function name | ||
| 125 | RRDRRAInfo.prototype.getCFName = function() { | ||
| 126 | return this.rrd_data.getCStringAt(this.rra_def_idx,20); | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | // ============================================================ | ||
| 131 | // RRD RRA handling class | ||
| 132 | function RRDRRA(rrd_data,rra_ptr_idx, | ||
| 133 | rra_info, | ||
| 134 | header_size,prev_row_cnts,ds_cnt) { | ||
| 135 | this.rrd_data=rrd_data; | ||
| 136 | this.rra_info=rra_info; | ||
| 137 | this.row_cnt=rra_info.row_cnt; | ||
| 138 | this.ds_cnt=ds_cnt; | ||
| 139 | |||
| 140 | var row_size=ds_cnt*8; | ||
| 141 | |||
| 142 | this.base_rrd_db_idx=header_size+prev_row_cnts*row_size; | ||
| 143 | |||
| 144 | // get imediately, since it will be needed often | ||
| 145 | this.cur_row=rrd_data.getLongAt(rra_ptr_idx); | ||
| 146 | |||
| 147 | // calculate idx relative to base_rrd_db_idx | ||
| 148 | // mostly used internally | ||
| 149 | this.calc_idx = function(row_idx,ds_idx) { | ||
| 150 | if ((row_idx>=0) && (row_idx<this.row_cnt)) { | ||
| 151 | if ((ds_idx>=0) && (ds_idx<ds_cnt)){ | ||
| 152 | // it is round robin, starting from cur_row+1 | ||
| 153 | var real_row_idx=row_idx+this.cur_row+1; | ||
| 154 | if (real_row_idx>=this.row_cnt) real_row_idx-=this.row_cnt; | ||
| 155 | return row_size*real_row_idx+ds_idx*8; | ||
| 156 | } else { | ||
| 157 | throw RangeError("DS idx ("+ row_idx +") out of range [0-" + ds_cnt +")."); | ||
| 158 | } | ||
| 159 | } else { | ||
| 160 | throw RangeError("Row idx ("+ row_idx +") out of range [0-" + this.row_cnt +")."); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | RRDRRA.prototype.getIdx = function() { | ||
| 166 | return this.rra_info.getIdx(); | ||
| 167 | } | ||
| 168 | |||
| 169 | // Get number of rows/columns | ||
| 170 | RRDRRA.prototype.getNrRows = function() { | ||
| 171 | return this.row_cnt; | ||
| 172 | } | ||
| 173 | RRDRRA.prototype.getNrDSs = function() { | ||
| 174 | return this.ds_cnt; | ||
| 175 | } | ||
| 176 | |||
| 177 | // Get RRA step (expressed in seconds) | ||
| 178 | RRDRRA.prototype.getStep = function() { | ||
| 179 | return this.rra_info.getStep(); | ||
| 180 | } | ||
| 181 | |||
| 182 | // Get consolidation function name | ||
| 183 | RRDRRA.prototype.getCFName = function() { | ||
| 184 | return this.rra_info.getCFName(); | ||
| 185 | } | ||
| 186 | |||
| 187 | RRDRRA.prototype.getEl = function(row_idx,ds_idx) { | ||
| 188 | return this.rrd_data.getDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); | ||
| 189 | } | ||
| 190 | |||
| 191 | // Low precision version of getEl | ||
| 192 | // Uses getFastDoubleAt | ||
| 193 | RRDRRA.prototype.getElFast = function(row_idx,ds_idx) { | ||
| 194 | return this.rrd_data.getFastDoubleAt(this.base_rrd_db_idx+this.calc_idx(row_idx,ds_idx)); | ||
| 195 | } | ||
| 196 | |||
| 197 | // ============================================================ | ||
| 198 | // RRD Header handling class | ||
| 199 | function RRDHeader(rrd_data) { | ||
| 200 | this.rrd_data=rrd_data; | ||
| 201 | this.validate_rrd(); | ||
| 202 | this.load_header(); | ||
| 203 | this.calc_idxs(); | ||
| 204 | } | ||
| 205 | |||
| 206 | // Internal, used for initialization | ||
| 207 | RRDHeader.prototype.validate_rrd = function() { | ||
| 208 | if (this.rrd_data.getCStringAt(0,4)!=="RRD") throw new InvalidRRD("Wrong magic id."); | ||
| 209 | |||
| 210 | this.rrd_version=this.rrd_data.getCStringAt(4,5); | ||
| 211 | if ((this.rrd_version!=="0003")&&(this.rrd_version!=="0004")) { | ||
| 212 | throw new InvalidRRD("Unsupported RRD version "+this.rrd_version+"."); | ||
| 213 | } | ||
| 214 | |||
| 215 | if (this.rrd_data.getDoubleAt(12)==8.642135e+130) { | ||
| 216 | this.rrd_align=32; | ||
| 217 | } else if (this.rrd_data.getDoubleAt(16)==8.642135e+130) { | ||
| 218 | this.rrd_align=64; | ||
| 219 | } else { | ||
| 220 | throw new InvalidRRD("Unsupported platform."); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | // Internal, used for initialization | ||
| 225 | RRDHeader.prototype.load_header = function() { | ||
| 226 | if (this.rrd_align==32) { | ||
| 227 | this.ds_cnt=this.rrd_data.getLongAt(20,false); | ||
| 228 | this.rra_cnt=this.rrd_data.getLongAt(24,false); | ||
| 229 | this.pdp_step=this.rrd_data.getLongAt(28,false); | ||
| 230 | // 8*10 unused values follow | ||
| 231 | this.top_header_size=112; | ||
| 232 | } else { | ||
| 233 | //get only the low 32 bits, the high 32 should always be 0 | ||
| 234 | this.ds_cnt=this.rrd_data.getLongAt(24,false); | ||
| 235 | this.rra_cnt=this.rrd_data.getLongAt(32,false); | ||
| 236 | this.pdp_step=this.rrd_data.getLongAt(40,false); | ||
| 237 | // 8*10 unused values follow | ||
| 238 | this.top_header_size=128; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | // Internal, used for initialization | ||
| 243 | RRDHeader.prototype.calc_idxs = function() { | ||
| 244 | this.ds_def_idx=this.top_header_size; | ||
| 245 | // char ds_nam[20], char dst[20], unival par[10] | ||
| 246 | this.ds_el_size=120; | ||
| 247 | |||
| 248 | this.rra_def_idx=this.ds_def_idx+this.ds_el_size*this.ds_cnt; | ||
| 249 | // char cf_nam[20], uint row_cnt, uint pdp_cnt, unival par[10] | ||
| 250 | this.row_cnt_idx; | ||
| 251 | if (this.rrd_align==32) { | ||
| 252 | this.rra_def_el_size=108; | ||
| 253 | this.row_cnt_idx=20; | ||
| 254 | } else { | ||
| 255 | this.rra_def_el_size=120; | ||
| 256 | this.row_cnt_idx=24; | ||
| 257 | } | ||
| 258 | |||
| 259 | this.live_head_idx=this.rra_def_idx+this.rra_def_el_size*this.rra_cnt; | ||
| 260 | // time_t last_up, int last_up_usec | ||
| 261 | if (this.rrd_align==32) { | ||
| 262 | this.live_head_size=8; | ||
| 263 | } else { | ||
| 264 | this.live_head_size=16; | ||
| 265 | } | ||
| 266 | |||
| 267 | this.pdp_prep_idx=this.live_head_idx+this.live_head_size; | ||
| 268 | // char last_ds[30], unival scratch[10] | ||
| 269 | this.pdp_prep_el_size=112; | ||
| 270 | |||
| 271 | this.cdp_prep_idx=this.pdp_prep_idx+this.pdp_prep_el_size*this.ds_cnt; | ||
| 272 | // unival scratch[10] | ||
| 273 | this.cdp_prep_el_size=80; | ||
| 274 | |||
| 275 | this.rra_ptr_idx=this.cdp_prep_idx+this.cdp_prep_el_size*this.ds_cnt*this.rra_cnt; | ||
| 276 | // uint cur_row | ||
| 277 | if (this.rrd_align==32) { | ||
| 278 | this.rra_ptr_el_size=4; | ||
| 279 | } else { | ||
| 280 | this.rra_ptr_el_size=8; | ||
| 281 | } | ||
| 282 | |||
| 283 | this.header_size=this.rra_ptr_idx+this.rra_ptr_el_size*this.rra_cnt; | ||
| 284 | } | ||
| 285 | |||
| 286 | // Optional initialization | ||
| 287 | // Read and calculate row counts | ||
| 288 | RRDHeader.prototype.load_row_cnts = function() { | ||
| 289 | this.rra_def_row_cnts=[]; | ||
| 290 | this.rra_def_row_cnt_sums=[]; // how many rows before me | ||
| 291 | for (var i=0; i<this.rra_cnt; i++) { | ||
| 292 | this.rra_def_row_cnts[i]=this.rrd_data.getLongAt(this.rra_def_idx+i*this.rra_def_el_size+this.row_cnt_idx,false); | ||
| 293 | if (i==0) { | ||
| 294 | this.rra_def_row_cnt_sums[i]=0; | ||
| 295 | } else { | ||
| 296 | this.rra_def_row_cnt_sums[i]=this.rra_def_row_cnt_sums[i-1]+this.rra_def_row_cnts[i-1]; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | // --------------------------- | ||
| 302 | // Start of user functions | ||
| 303 | |||
| 304 | RRDHeader.prototype.getMinStep = function() { | ||
| 305 | return this.pdp_step; | ||
| 306 | } | ||
| 307 | RRDHeader.prototype.getLastUpdate = function() { | ||
| 308 | return this.rrd_data.getLongAt(this.live_head_idx,false); | ||
| 309 | } | ||
| 310 | |||
| 311 | RRDHeader.prototype.getNrDSs = function() { | ||
| 312 | return this.ds_cnt; | ||
| 313 | } | ||
| 314 | RRDHeader.prototype.getDSNames = function() { | ||
| 315 | var ds_names=[] | ||
| 316 | for (var idx=0; idx<this.ds_cnt; idx++) { | ||
| 317 | var ds=this.getDSbyIdx(idx); | ||
| 318 | var ds_name=ds.getName() | ||
| 319 | ds_names.push(ds_name); | ||
| 320 | } | ||
| 321 | return ds_names; | ||
| 322 | } | ||
| 323 | RRDHeader.prototype.getDSbyIdx = function(idx) { | ||
| 324 | if ((idx>=0) && (idx<this.ds_cnt)) { | ||
| 325 | return new RRDDS(this.rrd_data,this.ds_def_idx+this.ds_el_size*idx,idx); | ||
| 326 | } else { | ||
| 327 | throw RangeError("DS idx ("+ idx +") out of range [0-" + this.ds_cnt +")."); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | RRDHeader.prototype.getDSbyName = function(name) { | ||
| 331 | for (var idx=0; idx<this.ds_cnt; idx++) { | ||
| 332 | var ds=this.getDSbyIdx(idx); | ||
| 333 | var ds_name=ds.getName() | ||
| 334 | if (ds_name==name) | ||
| 335 | return ds; | ||
| 336 | } | ||
| 337 | throw RangeError("DS name "+ name +" unknown."); | ||
| 338 | } | ||
| 339 | |||
| 340 | RRDHeader.prototype.getNrRRAs = function() { | ||
| 341 | return this.rra_cnt; | ||
| 342 | } | ||
| 343 | RRDHeader.prototype.getRRAInfo = function(idx) { | ||
| 344 | if ((idx>=0) && (idx<this.rra_cnt)) { | ||
| 345 | return new RRDRRAInfo(this.rrd_data, | ||
| 346 | this.rra_def_idx+idx*this.rra_def_el_size, | ||
| 347 | this.rrd_align,this.rra_def_row_cnts[idx],this.pdp_step, | ||
| 348 | idx); | ||
| 349 | } else { | ||
| 350 | throw RangeError("RRA idx ("+ idx +") out of range [0-" + this.rra_cnt +")."); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | // ============================================================ | ||
| 355 | // RRDFile class | ||
| 356 | // Given a BinaryFile, gives access to the RRD archive fields | ||
| 357 | // | ||
| 358 | // Arguments: | ||
| 359 | // bf must be an object compatible with the BinaryFile interface | ||
| 360 | function RRDFile(bf) { | ||
| 361 | var rrd_data=bf | ||
| 362 | |||
| 363 | this.rrd_header=new RRDHeader(rrd_data); | ||
| 364 | this.rrd_header.load_row_cnts(); | ||
| 365 | |||
| 366 | // =================================== | ||
| 367 | // Start of user functions | ||
| 368 | |||
| 369 | this.getMinStep = function() { | ||
| 370 | return this.rrd_header.getMinStep(); | ||
| 371 | } | ||
| 372 | this.getLastUpdate = function() { | ||
| 373 | return this.rrd_header.getLastUpdate(); | ||
| 374 | } | ||
| 375 | |||
| 376 | this.getNrDSs = function() { | ||
| 377 | return this.rrd_header.getNrDSs(); | ||
| 378 | } | ||
| 379 | this.getDSNames = function() { | ||
| 380 | return this.rrd_header.getDSNames(); | ||
| 381 | } | ||
| 382 | this.getDS = function(id) { | ||
| 383 | if (typeof id == "number") { | ||
| 384 | return this.rrd_header.getDSbyIdx(id); | ||
| 385 | } else { | ||
| 386 | return this.rrd_header.getDSbyName(id); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | this.getNrRRAs = function() { | ||
| 391 | return this.rrd_header.getNrRRAs(); | ||
| 392 | } | ||
| 393 | |||
| 394 | this.getRRAInfo = function(idx) { | ||
| 395 | return this.rrd_header.getRRAInfo(idx); | ||
| 396 | } | ||
| 397 | |||
| 398 | this.getRRA = function(idx) { | ||
| 399 | var rra_info=this.rrd_header.getRRAInfo(idx); | ||
| 400 | return new RRDRRA(rrd_data, | ||
| 401 | this.rrd_header.rra_ptr_idx+idx*this.rrd_header.rra_ptr_el_size, | ||
| 402 | rra_info, | ||
| 403 | this.rrd_header.header_size, | ||
| 404 | this.rrd_header.rra_def_row_cnt_sums[idx], | ||
| 405 | this.rrd_header.ds_cnt); | ||
| 406 | } | ||
| 407 | |||
| 408 | } | ||
diff --git a/js/sprintf.js b/js/sprintf.js new file mode 100644 index 0000000..4d61fa2 --- /dev/null +++ b/js/sprintf.js | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | **/ | ||
| 18 | |||
| 19 | "use strict"; | ||
| 20 | |||
| 21 | function sprintf() | ||
| 22 | { | ||
| 23 | var argc = 0; | ||
| 24 | var args = arguments; | ||
| 25 | var fmt = args[argc++]; | ||
| 26 | |||
| 27 | function lpad (str, padString, length) | ||
| 28 | { | ||
| 29 | while (str.length < length) | ||
| 30 | str = padString + str; | ||
| 31 | return str; | ||
| 32 | }; | ||
| 33 | |||
| 34 | function format (match, width, dot, precision, length, conversion) | ||
| 35 | { | ||
| 36 | if (match === '%%') return '%'; | ||
| 37 | |||
| 38 | var value = args[argc++]; | ||
| 39 | var prefix; | ||
| 40 | |||
| 41 | if (width === undefined) | ||
| 42 | width = 0; | ||
| 43 | else | ||
| 44 | width = +width; | ||
| 45 | |||
| 46 | if (precision === undefined) | ||
| 47 | precision = conversion == 'd' ? 0 : 6; | ||
| 48 | else | ||
| 49 | precision = +precision; | ||
| 50 | |||
| 51 | switch (conversion) { | ||
| 52 | case 's': | ||
| 53 | case 'c': | ||
| 54 | return value; | ||
| 55 | break; | ||
| 56 | case 'd': | ||
| 57 | return parseInt(value, 10); | ||
| 58 | break; | ||
| 59 | case 'e': | ||
| 60 | prefix = value < 0 ? '-' : ''; | ||
| 61 | return lpad(prefix+Math.abs(value).toExponential(precision),' ',width); | ||
| 62 | break; | ||
| 63 | case 'F': | ||
| 64 | case 'f': | ||
| 65 | prefix = value < 0 ? '-' : ''; | ||
| 66 | return lpad(prefix+Math.abs(value).toFixed(precision),' ',width); | ||
| 67 | break; | ||
| 68 | case 'g': | ||
| 69 | prefix = value < 0 ? '-' : ''; | ||
| 70 | return lpad(prefix+Math.abs(value).toPrecision(precision),' ',width); | ||
| 71 | break; | ||
| 72 | default: | ||
| 73 | return match; | ||
| 74 | } | ||
| 75 | |||
| 76 | }; | ||
| 77 | return fmt.replace(/%(\d+)?(\.(\d+))?(l?)([%scdfFeg])/g,format); | ||
| 78 | }; | ||
diff --git a/js/strftime.js b/js/strftime.js new file mode 100644 index 0000000..0933c27 --- /dev/null +++ b/js/strftime.js | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | /** | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify it | ||
| 4 | * under the terms of the GNU General Public License as published by the Free | ||
| 5 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 6 | * any later version. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | |||
| 13 | * You should have received a copy of the GNU General Public License along | ||
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 15 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 16 | |||
| 17 | **/ | ||
| 18 | |||
| 19 | "use strict"; | ||
| 20 | |||
| 21 | function strftime (fmt, time) | ||
| 22 | { | ||
| 23 | var d = new Date(time*1000); | ||
| 24 | |||
| 25 | var days = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; | ||
| 26 | var fdays = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; | ||
| 27 | var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; | ||
| 28 | var fmonths = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | ||
| 29 | |||
| 30 | function pad2 (number) | ||
| 31 | { | ||
| 32 | return (number < 10 ? '0' : '') + number | ||
| 33 | }; | ||
| 34 | |||
| 35 | function pad3(number) | ||
| 36 | { | ||
| 37 | return (number < 10 ? '00' : number < 100 ? '0' : '') + number | ||
| 38 | }; | ||
| 39 | |||
| 40 | function lpad (str, padString, length) | ||
| 41 | { | ||
| 42 | while (str.length < length) | ||
| 43 | str = padString + str; | ||
| 44 | return str; | ||
| 45 | }; | ||
| 46 | |||
| 47 | function format(match, opt) | ||
| 48 | { | ||
| 49 | if (match === '%%') return '%'; | ||
| 50 | |||
| 51 | switch (opt) { | ||
| 52 | case 'a': | ||
| 53 | return days[d.getDay()]; | ||
| 54 | break; | ||
| 55 | case 'A': | ||
| 56 | return fdays[d.getDay()]; | ||
| 57 | break; | ||
| 58 | case 'b': | ||
| 59 | return months[d.getMonth()]; | ||
| 60 | break; | ||
| 61 | case 'B': | ||
| 62 | return fmonths[d.getMonth()]; | ||
| 63 | break; | ||
| 64 | case 'c': | ||
| 65 | return d.toLocaleString(); | ||
| 66 | break; | ||
| 67 | case 'd': | ||
| 68 | return pad2(d.getDate()); | ||
| 69 | break; | ||
| 70 | case 'H': | ||
| 71 | return pad2(d.getHours()); | ||
| 72 | break; | ||
| 73 | case 'I': | ||
| 74 | var hours = d.getHours()%12; | ||
| 75 | return pad2(hours === 0 ? 12 : hours); | ||
| 76 | break; | ||
| 77 | case 'j': | ||
| 78 | var d01 = new Date (d.getFullYear(), 0, 1); | ||
| 79 | return pad3(Math.ceil((d.getTime()-d01.getTime())/86400000)+1); | ||
| 80 | break; | ||
| 81 | case 'm': | ||
| 82 | return pad2(d.getMonth()); | ||
| 83 | break; | ||
| 84 | case 'M': | ||
| 85 | return pad2(d.getMinutes()); | ||
| 86 | break; | ||
| 87 | case 'p': | ||
| 88 | return d.getHours() >= 12 ? 'PM' : 'AM'; | ||
| 89 | break; | ||
| 90 | case 's': | ||
| 91 | return pad2(d.getSeconds()); | ||
| 92 | break; | ||
| 93 | case 'S': | ||
| 94 | return d.getTime()/1000; | ||
| 95 | break; | ||
| 96 | case 'u': | ||
| 97 | return d.getDay() === 0 ? 7 : d.getDay(); | ||
| 98 | break; | ||
| 99 | case 'U': | ||
| 100 | var d01 = new Date(d.getFullYear(),0,1); | ||
| 101 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 6 - d.getDay())/7)); | ||
| 102 | break; | ||
| 103 | case 'V': | ||
| 104 | var d01 = new Date(d.getFullYear(), 0, 1); | ||
| 105 | var w = Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7); | ||
| 106 | var d31 = new Date(d.getFullYear(), 11, 31); | ||
| 107 | if (d01.getDay() < 4 && d01.getDay() > 1) w++; | ||
| 108 | if (w === 53 && d31.getDay() < 4) { | ||
| 109 | w = 1; | ||
| 110 | } else if (w === 0) { | ||
| 111 | d31 = new Date(d.getFullYear()-1, 11, 31); | ||
| 112 | d01 = new Date(d31.getFullYear(), 0, 1); | ||
| 113 | w = Math.round((Math.ceil((d31.getTime()-d01.getTime())/86400000)+1 + 7 - (d31.getDay() === 0 ? 7 : d31.getDay()))/7); | ||
| 114 | if (d01.getDay() < 4 && d01.getDay() > 1) w++; | ||
| 115 | if (w === 53 && d31.getDay() < 4) w = 1; | ||
| 116 | } | ||
| 117 | return pad2(w); | ||
| 118 | break; | ||
| 119 | case 'w': | ||
| 120 | return d.getDay(); | ||
| 121 | break; | ||
| 122 | case 'W': | ||
| 123 | var d01 = new Date(d.getFullYear(),0,1); | ||
| 124 | return pad2(Math.round((Math.ceil((d.getTime()-d01.getTime())/86400000)+1 + 7 - (d.getDay() === 0 ? 7 : d.getDay()))/7)); | ||
| 125 | break; | ||
| 126 | case 'x': | ||
| 127 | return pad2(d.getDate())+'/'+pad2(d.getMonth())+'/'+d.getFullYear() | ||
| 128 | break; | ||
| 129 | case 'X': | ||
| 130 | return pad2(d.getHours())+':'+pad2(d.getMinutes())+':'+pad2(d.getSeconds()); | ||
| 131 | break; | ||
| 132 | case 'y': | ||
| 133 | return pad2(d.getFullYear()%100); | ||
| 134 | break; | ||
| 135 | case 'Y': | ||
| 136 | return d.getFullYear(); | ||
| 137 | break; | ||
| 138 | case 'Z': | ||
| 139 | return d.toString().replace(/^.*\(([^)]+)\)$/, '$1'); | ||
| 140 | break; | ||
| 141 | default: | ||
| 142 | return match; | ||
| 143 | } | ||
| 144 | }; | ||
| 145 | return fmt.replace(/%([aAbBcdHIjmMpsSUVwWxXyYZ%])/g, format); | ||
| 146 | }; | ||
