1/** 2 * JustGage - animated gauges using RaphaelJS 3 * Check http://www.justgage.com for official releases 4 * Licensed under MIT. 5 * @author Bojan Djuricic (@Toorshia) 6 **/ 7 8JustGage = function(config) { 9 10 var obj = this; 11 12 // Helps in case developer wants to debug it. unobtrusive 13 if (config === null || config === undefined) { 14 console.log('* justgage: Make sure to pass options to the constructor!'); 15 return false; 16 } 17 18 var node; 19 20 if (config.id !== null && config.id !== undefined) { 21 node = document.getElementById(config.id); 22 if (!node) { 23 console.log('* justgage: No element with id : %s found', config.id); 24 return false; 25 } 26 } else if (config.parentNode !== null && config.parentNode !== undefined) { 27 node = config.parentNode; 28 } else { 29 console.log('* justgage: Make sure to pass the existing element id or parentNode to the constructor.'); 30 return false; 31 } 32 33 var dataset = node.dataset ? node.dataset : {}; 34 35 // check for defaults 36 var defaults = (config.defaults !== null && config.defaults !== undefined) ? config.defaults : false; 37 if (defaults !== false) { 38 config = extend({}, config, defaults); 39 delete config.defaults; 40 } 41 42 // configurable parameters 43 obj.config = { 44 // id : string 45 // this is container element id 46 id: config.id, 47 48 // value : float 49 // value gauge is showing 50 value: kvLookup('value', config, dataset, 0, 'float'), 51 52 // defaults : bool 53 // defaults parameter to use 54 defaults: kvLookup('defaults', config, dataset, 0, false), 55 56 // parentNode : node object 57 // this is container element 58 parentNode: kvLookup('parentNode', config, dataset, null), 59 60 // width : int 61 // gauge width 62 width: kvLookup('width', config, dataset, null), 63 64 // height : int 65 // gauge height 66 height: kvLookup('height', config, dataset, null), 67 68 // title : string 69 // gauge title 70 title: kvLookup('title', config, dataset, ""), 71 72 // titleFontColor : string 73 // color of gauge title 74 titleFontColor: kvLookup('titleFontColor', config, dataset, "#999999"), 75 76 // titleFontFamily : string 77 // color of gauge title 78 titleFontFamily: kvLookup('titleFontFamily', config, dataset, "sans-serif"), 79 80 // titlePosition : string 81 // 'above' or 'below' 82 titlePosition: kvLookup('titlePosition', config, dataset, "above"), 83 84 // valueFontColor : string 85 // color of label showing current value 86 valueFontColor: kvLookup('valueFontColor', config, dataset, "#010101"), 87 88 // valueFontFamily : string 89 // color of label showing current value 90 valueFontFamily: kvLookup('valueFontFamily', config, dataset, "Arial"), 91 92 // symbol : string 93 // special symbol to show next to value 94 symbol: kvLookup('symbol', config, dataset, ''), 95 96 // min : float 97 // min value 98 min: kvLookup('min', config, dataset, 0, 'float'), 99 100 // max : float 101 // max value 102 max: kvLookup('max', config, dataset, 100, 'float'), 103 104 // reverse : bool 105 // reverse min and max 106 reverse: kvLookup('reverse', config, dataset, false), 107 108 // humanFriendlyDecimal : int 109 // number of decimal places for our human friendly number to contain 110 humanFriendlyDecimal: kvLookup('humanFriendlyDecimal', config, dataset, 0), 111 112 113 // textRenderer: func 114 // function applied before rendering text 115 textRenderer: kvLookup('textRenderer', config, dataset, null), 116 117 // gaugeWidthScale : float 118 // width of the gauge element 119 gaugeWidthScale: kvLookup('gaugeWidthScale', config, dataset, 1.0), 120 121 // gaugeColor : string 122 // background color of gauge element 123 gaugeColor: kvLookup('gaugeColor', config, dataset, "#edebeb"), 124 125 // label : string 126 // text to show below value 127 label: kvLookup('label', config, dataset, ''), 128 129 // labelFontColor : string 130 // color of label showing label under value 131 labelFontColor: kvLookup('labelFontColor', config, dataset, "#b3b3b3"), 132 133 // shadowOpacity : int 134 // 0 ~ 1 135 shadowOpacity: kvLookup('shadowOpacity', config, dataset, 0.2), 136 137 // shadowSize: int 138 // inner shadow size 139 shadowSize: kvLookup('shadowSize', config, dataset, 5), 140 141 // shadowVerticalOffset : int 142 // how much shadow is offset from top 143 shadowVerticalOffset: kvLookup('shadowVerticalOffset', config, dataset, 3), 144 145 // levelColors : string[] 146 // colors of indicator, from lower to upper, in RGB format 147 levelColors: kvLookup('levelColors', config, dataset, ["#a9d70b", "#f9c802", "#ff0000"], 'array', ','), 148 149 // startAnimationTime : int 150 // length of initial animation 151 startAnimationTime: kvLookup('startAnimationTime', config, dataset, 700), 152 153 // startAnimationType : string 154 // type of initial animation (linear, >, <, <>, bounce) 155 startAnimationType: kvLookup('startAnimationType', config, dataset, '>'), 156 157 // refreshAnimationTime : int 158 // length of refresh animation 159 refreshAnimationTime: kvLookup('refreshAnimationTime', config, dataset, 700), 160 161 // refreshAnimationType : string 162 // type of refresh animation (linear, >, <, <>, bounce) 163 refreshAnimationType: kvLookup('refreshAnimationType', config, dataset, '>'), 164 165 // donutStartAngle : int 166 // angle to start from when in donut mode 167 donutStartAngle: kvLookup('donutStartAngle', config, dataset, 90), 168 169 // valueMinFontSize : int 170 // absolute minimum font size for the value 171 valueMinFontSize: kvLookup('valueMinFontSize', config, dataset, 16), 172 173 // titleMinFontSize 174 // absolute minimum font size for the title 175 titleMinFontSize: kvLookup('titleMinFontSize', config, dataset, 10), 176 177 // labelMinFontSize 178 // absolute minimum font size for the label 179 labelMinFontSize: kvLookup('labelMinFontSize', config, dataset, 10), 180 181 // minLabelMinFontSize 182 // absolute minimum font size for the minimum label 183 minLabelMinFontSize: kvLookup('minLabelMinFontSize', config, dataset, 10), 184 185 // maxLabelMinFontSize 186 // absolute minimum font size for the maximum label 187 maxLabelMinFontSize: kvLookup('maxLabelMinFontSize', config, dataset, 10), 188 189 // hideValue : bool 190 // hide value text 191 hideValue: kvLookup('hideValue', config, dataset, false), 192 193 // hideMinMax : bool 194 // hide min and max values 195 hideMinMax: kvLookup('hideMinMax', config, dataset, false), 196 197 // hideInnerShadow : bool 198 // hide inner shadow 199 hideInnerShadow: kvLookup('hideInnerShadow', config, dataset, false), 200 201 // humanFriendly : bool 202 // convert large numbers for min, max, value to human friendly (e.g. 1234567 -> 1.23M) 203 humanFriendly: kvLookup('humanFriendly', config, dataset, false), 204 205 // noGradient : bool 206 // whether to use gradual color change for value, or sector-based 207 noGradient: kvLookup('noGradient', config, dataset, false), 208 209 // donut : bool 210 // show full donut gauge 211 donut: kvLookup('donut', config, dataset, false), 212 213 // relativeGaugeSize : bool 214 // whether gauge size should follow changes in container element size 215 relativeGaugeSize: kvLookup('relativeGaugeSize', config, dataset, false), 216 217 // counter : bool 218 // animate level number change 219 counter: kvLookup('counter', config, dataset, false), 220 221 // decimals : int 222 // number of digits after floating point 223 decimals: kvLookup('decimals', config, dataset, 0), 224 225 // customSectors : [] of objects 226 // number of digits after floating point 227 customSectors: kvLookup('customSectors', config, dataset, []), 228 229 // formatNumber: boolean 230 // formats numbers with commas where appropriate 231 formatNumber: kvLookup('formatNumber', config, dataset, false), 232 233 // pointer : bool 234 // show value pointer 235 pointer: kvLookup('pointer', config, dataset, false), 236 237 // pointerOptions : object 238 // define pointer look 239 pointerOptions: kvLookup('pointerOptions', config, dataset, []) 240 }; 241 242 // variables 243 var 244 canvasW, 245 canvasH, 246 widgetW, 247 widgetH, 248 aspect, 249 dx, 250 dy, 251 titleFontSize, 252 titleX, 253 titleY, 254 valueFontSize, 255 valueX, 256 valueY, 257 labelFontSize, 258 labelX, 259 labelY, 260 minFontSize, 261 minX, 262 minY, 263 maxFontSize, 264 maxX, 265 maxY; 266 267 // overflow values 268 if (obj.config.value > obj.config.max) obj.config.value = obj.config.max; 269 if (obj.config.value < obj.config.min) obj.config.value = obj.config.min; 270 obj.originalValue = kvLookup('value', config, dataset, -1, 'float'); 271 272 // create canvas 273 if (obj.config.id !== null && (document.getElementById(obj.config.id)) !== null) { 274 obj.canvas = Raphael(obj.config.id, "100%", "100%"); 275 } else if (obj.config.parentNode !== null) { 276 obj.canvas = Raphael(obj.config.parentNode, "100%", "100%"); 277 } 278 279 if (obj.config.relativeGaugeSize === true) { 280 obj.canvas.setViewBox(0, 0, 200, 150, true); 281 } 282 283 // canvas dimensions 284 if (obj.config.relativeGaugeSize === true) { 285 canvasW = 200; 286 canvasH = 150; 287 } else if (obj.config.width !== null && obj.config.height !== null) { 288 canvasW = obj.config.width; 289 canvasH = obj.config.height; 290 } else if (obj.config.parentNode !== null) { 291 obj.canvas.setViewBox(0, 0, 200, 150, true); 292 canvasW = 200; 293 canvasH = 150; 294 } else { 295 canvasW = getStyle(document.getElementById(obj.config.id), "width").slice(0, -2) * 1; 296 canvasH = getStyle(document.getElementById(obj.config.id), "height").slice(0, -2) * 1; 297 } 298 299 // widget dimensions 300 if (obj.config.donut === true) { 301 302 // DONUT ******************************* 303 304 // width more than height 305 if (canvasW > canvasH) { 306 widgetH = canvasH; 307 widgetW = widgetH; 308 // width less than height 309 } else if (canvasW < canvasH) { 310 widgetW = canvasW; 311 widgetH = widgetW; 312 // if height don't fit, rescale both 313 if (widgetH > canvasH) { 314 aspect = widgetH / canvasH; 315 widgetH = widgetH / aspect; 316 widgetW = widgetH / aspect; 317 } 318 // equal 319 } else { 320 widgetW = canvasW; 321 widgetH = widgetW; 322 } 323 324 // delta 325 dx = (canvasW - widgetW) / 2; 326 dy = (canvasH - widgetH) / 2; 327 328 // title 329 titleFontSize = ((widgetH / 8) > 10) ? (widgetH / 10) : 10; 330 titleX = dx + widgetW / 2; 331 titleY = dy + widgetH / 11; 332 333 // value 334 valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 5.4) : 18; 335 valueX = dx + widgetW / 2; 336 if (obj.config.label !== '') { 337 valueY = dy + widgetH / 1.85; 338 } else { 339 valueY = dy + widgetH / 1.7; 340 } 341 342 // label 343 labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; 344 labelX = dx + widgetW / 2; 345 labelY = valueY + labelFontSize; 346 347 // min 348 minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; 349 minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * obj.config.gaugeWidthScale) / 2; 350 minY = labelY; 351 352 // max 353 maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10; 354 maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * obj.config.gaugeWidthScale) / 2; 355 maxY = labelY; 356 357 } else { 358 // HALF ******************************* 359 360 // width more than height 361 if (canvasW > canvasH) { 362 widgetH = canvasH; 363 widgetW = widgetH * 1.25; 364 //if width doesn't fit, rescale both 365 if (widgetW > canvasW) { 366 aspect = widgetW / canvasW; 367 widgetW = widgetW / aspect; 368 widgetH = widgetH / aspect; 369 } 370 // width less than height 371 } else if (canvasW < canvasH) { 372 widgetW = canvasW; 373 widgetH = widgetW / 1.25; 374 // if height don't fit, rescale both 375 if (widgetH > canvasH) { 376 aspect = widgetH / canvasH; 377 widgetH = widgetH / aspect; 378 widgetW = widgetH / aspect; 379 } 380 // equal 381 } else { 382 widgetW = canvasW; 383 widgetH = widgetW * 0.75; 384 } 385 386 // delta 387 dx = (canvasW - widgetW) / 2; 388 dy = (canvasH - widgetH) / 2; 389 if (obj.config.titlePosition === 'below') { 390 // shift whole thing down 391 dy -= (widgetH / 6.4); 392 } 393 394 // title 395 titleFontSize = ((widgetH / 8) > obj.config.titleMinFontSize) ? (widgetH / 10) : obj.config.titleMinFontSize; 396 titleX = dx + widgetW / 2; 397 titleY = dy + (obj.config.titlePosition === 'below' ? (widgetH * 1.07) : (widgetH / 6.4)); 398 399 // value 400 valueFontSize = ((widgetH / 6.5) > obj.config.valueMinFontSize) ? (widgetH / 6.5) : obj.config.valueMinFontSize; 401 valueX = dx + widgetW / 2; 402 valueY = dy + widgetH / 1.275; 403 404 // label 405 labelFontSize = ((widgetH / 16) > obj.config.labelMinFontSize) ? (widgetH / 16) : obj.config.labelMinFontSize; 406 labelX = dx + widgetW / 2; 407 labelY = valueY + valueFontSize / 2 + 5; 408 409 // min 410 minFontSize = ((widgetH / 16) > obj.config.minLabelMinFontSize) ? (widgetH / 16) : obj.config.minLabelMinFontSize; 411 minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * obj.config.gaugeWidthScale) / 2; 412 minY = labelY; 413 414 // max 415 maxFontSize = ((widgetH / 16) > obj.config.maxLabelMinFontSize) ? (widgetH / 16) : obj.config.maxLabelMinFontSize; 416 maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * obj.config.gaugeWidthScale) / 2; 417 maxY = labelY; 418 } 419 420 // parameters 421 obj.params = { 422 canvasW: canvasW, 423 canvasH: canvasH, 424 widgetW: widgetW, 425 widgetH: widgetH, 426 dx: dx, 427 dy: dy, 428 titleFontSize: titleFontSize, 429 titleX: titleX, 430 titleY: titleY, 431 valueFontSize: valueFontSize, 432 valueX: valueX, 433 valueY: valueY, 434 labelFontSize: labelFontSize, 435 labelX: labelX, 436 labelY: labelY, 437 minFontSize: minFontSize, 438 minX: minX, 439 minY: minY, 440 maxFontSize: maxFontSize, 441 maxX: maxX, 442 maxY: maxY 443 }; 444 445 // var clear 446 canvasW, canvasH, widgetW, widgetH, aspect, dx, dy, titleFontSize, titleX, titleY, valueFontSize, valueX, valueY, labelFontSize, labelX, labelY, minFontSize, minX, minY, maxFontSize, maxX, maxY = null; 447 448 // pki - custom attribute for generating gauge paths 449 obj.canvas.customAttributes.pki = function(value, min, max, w, h, dx, dy, gws, donut, reverse) { 450 451 var alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, path; 452 453 if (donut) { 454 alpha = (1 - 2 * (value - min) / (max - min)) * Math.PI; 455 Ro = w / 2 - w / 7; 456 Ri = Ro - w / 6.666666666666667 * gws; 457 458 Cx = w / 2 + dx; 459 Cy = h / 1.95 + dy; 460 461 Xo = w / 2 + dx + Ro * Math.cos(alpha); 462 Yo = h - (h - Cy) - Ro * Math.sin(alpha); 463 Xi = w / 2 + dx + Ri * Math.cos(alpha); 464 Yi = h - (h - Cy) - Ri * Math.sin(alpha); 465 466 path = "M" + (Cx - Ri) + "," + Cy + " "; 467 path += "L" + (Cx - Ro) + "," + Cy + " "; 468 if (value > ((max - min) / 2)) { 469 path += "A" + Ro + "," + Ro + " 0 0 1 " + (Cx + Ro) + "," + Cy + " "; 470 } 471 path += "A" + Ro + "," + Ro + " 0 0 1 " + Xo + "," + Yo + " "; 472 path += "L" + Xi + "," + Yi + " "; 473 if (value > ((max - min) / 2)) { 474 path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx + Ri) + "," + Cy + " "; 475 } 476 path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx - Ri) + "," + Cy + " "; 477 path += "Z "; 478 479 return { 480 path: path 481 }; 482 483 } else { 484 alpha = (1 - (value - min) / (max - min)) * Math.PI; 485 Ro = w / 2 - w / 10; 486 Ri = Ro - w / 6.666666666666667 * gws; 487 488 Cx = w / 2 + dx; 489 Cy = h / 1.25 + dy; 490 491 Xo = w / 2 + dx + Ro * Math.cos(alpha); 492 Yo = h - (h - Cy) - Ro * Math.sin(alpha); 493 Xi = w / 2 + dx + Ri * Math.cos(alpha); 494 Yi = h - (h - Cy) - Ri * Math.sin(alpha); 495 496 path = "M" + (Cx - Ri) + "," + Cy + " "; 497 path += "L" + (Cx - Ro) + "," + Cy + " "; 498 path += "A" + Ro + "," + Ro + " 0 0 1 " + Xo + "," + Yo + " "; 499 path += "L" + Xi + "," + Yi + " "; 500 path += "A" + Ri + "," + Ri + " 0 0 0 " + (Cx - Ri) + "," + Cy + " "; 501 path += "Z "; 502 503 return { 504 path: path 505 }; 506 } 507 508 // var clear
509 alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, path = null; 510 }; 511 512 // ndl - custom attribute for generating needle path 513 obj.canvas.customAttributes.ndl = function(value, min, max, w, h, dx, dy, gws, donut) { 514 515 var dlt = w * 3.5 / 100; 516 var dlb = w / 15; 517 var dw = w / 100; 518 519 if (obj.config.pointerOptions.toplength != null && obj.config.pointerOptions.toplength != undefined) dlt = obj.config.pointerOptions.toplength; 520 if (obj.config.pointerOptions.bottomlength != null && obj.config.pointerOptions.bottomlength != undefined) dlb = obj.config.pointerOptions.bottomlength; 521 if (obj.config.pointerOptions.bottomwidth != null && obj.config.pointerOptions.bottomwidth != undefined) dw = obj.config.pointerOptions.bottomwidth; 522 523 var alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, Xc, Yc, Xz, Yz, Xa, Ya, Xb, Yb, path; 524 525 if (donut) { 526 527 alpha = (1 - 2 * (value - min) / (max - min)) * Math.PI; 528 Ro = w / 2 - w / 7; 529 Ri = Ro - w / 6.666666666666667 * gws; 530 531 Cx = w / 2 + dx; 532 Cy = h / 1.95 + dy; 533 534 Xo = w / 2 + dx + Ro * Math.cos(alpha); 535 Yo = h - (h - Cy) - Ro * Math.sin(alpha); 536 Xi = w / 2 + dx + Ri * Math.cos(alpha); 537 Yi = h - (h - Cy) - Ri * Math.sin(alpha); 538 539 Xc = Xo + dlt * Math.cos(alpha); 540 Yc = Yo - dlt * Math.sin(alpha); 541 Xz = Xi - dlb * Math.cos(alpha); 542 Yz = Yi + dlb * Math.sin(alpha); 543 544 Xa = Xz + dw * Math.sin(alpha); 545 Ya = Yz + dw * Math.cos(alpha); 546 Xb = Xz - dw * Math.sin(alpha); 547 Yb = Yz - dw * Math.cos(alpha); 548 549 path = 'M' + Xa + ',' + Ya + ' '; 550 path += 'L' + Xb + ',' + Yb + ' '; 551 path += 'L' + Xc + ',' + Yc + ' '; 552 path += 'Z '; 553 554 return { 555 path: path 556 }; 557 558 } else { 559 alpha = (1 - (value - min) / (max - min)) * Math.PI; 560 Ro = w / 2 - w / 10; 561 Ri = Ro - w / 6.666666666666667 * gws; 562 563 Cx = w / 2 + dx; 564 Cy = h / 1.25 + dy; 565 566 Xo = w / 2 + dx + Ro * Math.cos(alpha); 567 Yo = h - (h - Cy) - Ro * Math.sin(alpha); 568 Xi = w / 2 + dx + Ri * Math.cos(alpha); 569 Yi = h - (h - Cy) - Ri * Math.sin(alpha); 570 571 Xc = Xo + dlt * Math.cos(alpha); 572 Yc = Yo - dlt * Math.sin(alpha); 573 Xz = Xi - dlb * Math.cos(alpha); 574 Yz = Yi + dlb * Math.sin(alpha); 575 576 Xa = Xz + dw * Math.sin(alpha); 577 Ya = Yz + dw * Math.cos(alpha); 578 Xb = Xz - dw * Math.sin(alpha); 579 Yb = Yz - dw * Math.cos(alpha); 580 581 path = 'M' + Xa + ',' + Ya + ' '; 582 path += 'L' + Xb + ',' + Yb + ' '; 583 path += 'L' + Xc + ',' + Yc + ' '; 584 path += 'Z '; 585 586 return { 587 path: path 588 }; 589 } 590 591 // var clear 592 alpha, Ro, Ri, Cx, Cy, Xo, Yo, Xi, Yi, Xc, Yc, Xz, Yz, Xa, Ya, Xb, Yb, path = null; 593 }; 594 595 // gauge 596 obj.gauge = obj.canvas.path().attr({ 597 "stroke": "none", 598 "fill": obj.config.gaugeColor, 599 pki: [ 600 obj.config.max, 601 obj.config.min, 602 obj.config.max, 603 obj.params.widgetW, 604 obj.params.widgetH, 605 obj.params.dx, 606 obj.params.dy, 607 obj.config.gaugeWidthScale, 608 obj.config.donut, 609 obj.config.reverse 610 ] 611 }); 612 613 // level 614 obj.level = obj.canvas.path().attr({ 615 "stroke": "none", 616 "fill": getColor(obj.config.value, (obj.config.value - obj.config.min) / (obj.config.max - obj.config.min), obj.config.levelColors, obj.config.noGradient, obj.config.customSectors), 617 pki: [ 618 obj.config.min, 619 obj.config.min, 620 obj.config.max, 621 obj.params.widgetW, 622 obj.params.widgetH, 623 obj.params.dx, 624 obj.params.dy, 625 obj.config.gaugeWidthScale, 626 obj.config.donut, 627 obj.config.reverse 628 ] 629 }); 630 if (obj.config.donut) { 631 obj.level.transform("r" + obj.config.donutStartAngle + ", " + (obj.params.widgetW / 2 + obj.params.dx) + ", " + (obj.params.widgetH / 1.95 + obj.params.dy)); 632 } 633 634 if (obj.config.pointer) { 635 // needle 636 obj.needle = obj.canvas.path().attr({ 637 "stroke": (obj.config.pointerOptions.stroke !== null && obj.config.pointerOptions.stroke !== undefined) ? obj.config.pointerOptions.stroke : "none", 638 "stroke-width": (obj.config.pointerOptions.stroke_width !== null && obj.config.pointerOptions.stroke_width !== undefined) ? obj.config.pointerOptions.stroke_width : 0, 639 "stroke-linecap": (obj.config.pointerOptions.stroke_linecap !== null && obj.config.pointerOptions.stroke_linecap !== undefined) ? obj.config.pointerOptions.stroke_linecap : "square", 640 "fill": (obj.config.pointerOptions.color !== null && obj.config.pointerOptions.color !== undefined) ? obj.config.pointerOptions.color : "#000000", 641 ndl: [ 642 obj.config.min, 643 obj.config.min, 644 obj.config.max, 645 obj.params.widgetW, 646 obj.params.widgetH, 647 obj.params.dx, 648 obj.params.dy, 649 obj.config.gaugeWidthScale, 650 obj.config.donut 651 ] 652 });
653 654 if (obj.config.donut) { 655 obj.needle.transform("r" + obj.config.donutStartAngle + ", " + (obj.params.widgetW / 2 + obj.params.dx) + ", " + (obj.params.widgetH / 1.95 + obj.params.dy)); 656 } 657 658 } 659 660 // title 661 obj.txtTitle = obj.canvas.text(obj.params.titleX, obj.params.titleY, obj.config.title); 662 obj.txtTitle.attr({ 663 "font-size": obj.params.titleFontSize, 664 "font-weight": "normal", 665 "font-family": obj.config.titleFontFamily, 666 "fill": obj.config.titleFontColor, 667 "fill-opacity": "1" 668 }); 669 setDy(obj.txtTitle, obj.params.titleFontSize, obj.params.titleY); 670 671 // value 672 obj.txtValue = obj.canvas.text(obj.params.valueX, obj.params.valueY, 0); 673 obj.txtValue.attr({ 674 "font-size": obj.params.valueFontSize, 675 "font-weight": "normal", 676 "font-family": obj.config.valueFontFamily, 677 "fill": obj.config.valueFontColor, 678 "fill-opacity": "0" 679 }); 680 setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY); 681 682 // label 683 obj.txtLabel = obj.canvas.text(obj.params.labelX, obj.params.labelY, obj.config.label); 684 obj.txtLabel.attr({ 685 "font-size": obj.params.labelFontSize, 686 "font-weight": "normal", 687 "font-family": "Arial", 688 "fill": obj.config.labelFontColor, 689 "fill-opacity": "0" 690 }); 691 setDy(obj.txtLabel, obj.params.labelFontSize, obj.params.labelY); 692 693 // min 694 var min = obj.config.min; 695 if (obj.config.reverse) { 696 min = obj.config.max; 697 } 698 699 obj.txtMinimum = min; 700 if (obj.config.humanFriendly) { 701 obj.txtMinimum = humanFriendlyNumber(min, obj.config.humanFriendlyDecimal); 702 } else if (obj.config.formatNumber) { 703 obj.txtMinimum = formatNumber(min); 704 } 705 obj.txtMin = obj.canvas.text(obj.params.minX, obj.params.minY, obj.txtMinimum); 706 obj.txtMin.attr({ 707 "font-size": obj.params.minFontSize, 708 "font-weight": "normal", 709 "font-family": "Arial", 710 "fill": obj.config.labelFontColor, 711 "fill-opacity": (obj.config.hideMinMax || obj.config.donut) ? "0" : "1" 712 }); 713 setDy(obj.txtMin, obj.params.minFontSize, obj.params.minY); 714 715 // max 716 var max = obj.config.max; 717 if (obj.config.reverse) { 718 max = obj.config.min; 719 } 720 obj.txtMaximum = max; 721 if (obj.config.humanFriendly) { 722 obj.txtMaximum = humanFriendlyNumber(max, obj.config.humanFriendlyDecimal); 723 } else if (obj.config.formatNumber) { 724 obj.txtMaximum = formatNumber(max); 725 } 726 obj.txtMax = obj.canvas.text(obj.params.maxX, obj.params.maxY, obj.txtMaximum); 727 obj.txtMax.attr({ 728 "font-size": obj.params.maxFontSize, 729 "font-weight": "normal", 730 "font-family": "Arial", 731 "fill": obj.config.labelFontColor, 732 "fill-opacity": (obj.config.hideMinMax || obj.config.donut) ? "0" : "1" 733 }); 734 setDy(obj.txtMax, obj.params.maxFontSize, obj.params.maxY); 735 736 var defs = obj.canvas.canvas.childNodes[1]; 737 var svg = "http://www.w3.org/2000/svg"; 738 739 if (ie !== 'undefined' && ie < 9) { 740 // VML mode - no SVG & SVG filter support 741 } else if (ie !== 'undefined') { 742 onCreateElementNsReady(function() { 743 obj.generateShadow(svg, defs); 744 }); 745 } else { 746 obj.generateShadow(svg, defs); 747 } 748 749 // var clear 750 defs, svg = null; 751 752 // set value to display 753 if (obj.config.textRenderer) { 754 obj.originalValue = obj.config.textRenderer(obj.originalValue); 755 } else if (obj.config.humanFriendly) { 756 obj.originalValue = humanFriendlyNumber(obj.originalValue, obj.config.humanFriendlyDecimal) + obj.config.symbol; 757 } else if (obj.config.formatNumber) { 758 obj.originalValue = formatNumber(obj.originalValue) + obj.config.symbol; 759 } else { 760 obj.originalValue = (obj.originalValue * 1).toFixed(obj.config.decimals) + obj.config.symbol; 761 } 762 763 if (obj.config.counter === true) { 764 //on each animation frame 765 eve.on("raphael.anim.frame." + (obj.level.id), function() { 766 var currentValue = obj.level.attr("pki")[0]; 767 if (obj.config.reverse) { 768 currentValue = (obj.config.max * 1) + (obj.config.min * 1) - (obj.level.attr("pki")[0] * 1); 769 } 770 if (obj.config.textRenderer) { 771 obj.txtValue.attr("text", obj.config.textRenderer(Math.floor(currentValue))); 772 } else if (obj.config.humanFriendly) { 773 obj.txtValue.attr("text", humanFriendlyNumber(Math.floor(currentValue), obj.config.humanFriendlyDecimal) + obj.config.symbol); 774 } else if (obj.config.formatNumber) { 775 obj.txtValue.attr("text", formatNumber(Math.floor(currentValue)) + obj.config.symbol); 776 } else { 777 obj.txtValue.attr("text", (currentValue * 1).toFixed(obj.config.decimals) + obj.config.symbol); 778 } 779 setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY); 780 currentValue = null; 781 });
782 //on animation end 783 eve.on("raphael.anim.finish." + (obj.level.id), function() { 784 obj.txtValue.attr({ 785 "text": obj.originalValue 786 }); 787 setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY); 788 }); 789 } else { 790 //on animation start 791 eve.on("raphael.anim.start." + (obj.level.id), function() { 792 obj.txtValue.attr({ 793 "text": obj.originalValue 794 }); 795 setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY); 796 }); 797 } 798 799 // animate gauge level, value & label 800 var rvl = obj.config.value; 801 if (obj.config.reverse) { 802 rvl = (obj.config.max * 1) + (obj.config.min * 1) - (obj.config.value * 1); 803 } 804 obj.level.animate({ 805 pki: [ 806 rvl, 807 obj.config.min, 808 obj.config.max, 809 obj.params.widgetW, 810 obj.params.widgetH, 811 obj.params.dx, 812 obj.params.dy, 813 obj.config.gaugeWidthScale, 814 obj.config.donut, 815 obj.config.reverse 816 ] 817 }, obj.config.startAnimationTime, obj.config.startAnimationType); 818 819 if (obj.config.pointer) { 820 obj.needle.animate({ 821 ndl: [ 822 rvl, 823 obj.config.min, 824 obj.config.max, 825 obj.params.widgetW, 826 obj.params.widgetH, 827 obj.params.dx, 828 obj.params.dy, 829 obj.config.gaugeWidthScale, 830 obj.config.donut 831 ] 832 }, obj.config.startAnimationTime, obj.config.startAnimationType); 833 } 834 835 obj.txtValue.animate({ 836 "fill-opacity": (obj.config.hideValue) ? "0" : "1" 837 }, obj.config.startAnimationTime, obj.config.startAnimationType); 838 obj.txtLabel.animate({ 839 "fill-opacity": "1" 840 }, obj.config.startAnimationTime, obj.config.startAnimationType); 841}; 842 843/** Refresh gauge level */ 844JustGage.prototype.refresh = function(val, max) { 845 846 var obj = this; 847 var displayVal, color, max = max || null; 848 849 // set new max 850 if (max !== null) { 851 obj.config.max = max; 852 // TODO: update customSectors 853 854 obj.txtMaximum = obj.config.max; 855 if (obj.config.humanFriendly) { 856 obj.txtMaximum = humanFriendlyNumber(obj.config.max, obj.config.humanFriendlyDecimal); 857 } else if (obj.config.formatNumber) { 858 obj.txtMaximum = formatNumber(obj.config.max); 859 } 860 if (!obj.config.reverse) { 861 obj.txtMax.attr({ 862 "text": obj.txtMaximum 863 }); 864 setDy(obj.txtMax, obj.params.maxFontSize, obj.params.maxY); 865 } else { 866 obj.txtMin.attr({ 867 "text": obj.txtMaximum 868 }); 869 setDy(obj.txtMin, obj.params.minFontSize, obj.params.minY); 870 } 871 } 872 873 // overflow values 874 displayVal = val; 875 if ((val * 1) > (obj.config.max * 1)) { 876 val = (obj.config.max * 1); 877 } 878 if ((val * 1) < (obj.config.min * 1)) { 879 val = (obj.config.min * 1); 880 } 881 882 color = getColor(val, (val - obj.config.min) / (obj.config.max - obj.config.min), obj.config.levelColors, obj.config.noGradient, obj.config.customSectors); 883 884 if (obj.config.textRenderer) { 885 displayVal = obj.config.textRenderer(displayVal); 886 } else if (obj.config.humanFriendly) { 887 displayVal = humanFriendlyNumber(displayVal, obj.config.humanFriendlyDecimal) + obj.config.symbol; 888 } else if (obj.config.formatNumber) { 889 displayVal = formatNumber((displayVal * 1).toFixed(obj.config.decimals)) + obj.config.symbol; 890 } else { 891 displayVal = (displayVal * 1).toFixed(obj.config.decimals) + obj.config.symbol; 892 } 893 obj.originalValue = displayVal; 894 obj.config.value = val * 1; 895 896 if (!obj.config.counter) { 897 obj.txtValue.attr({ 898 "text": displayVal 899 }); 900 setDy(obj.txtValue, obj.params.valueFontSize, obj.params.valueY); 901 } 902 903 var rvl = obj.config.value; 904 if (obj.config.reverse) { 905 rvl = (obj.config.max * 1) + (obj.config.min * 1) - (obj.config.value * 1); 906 } 907 obj.level.animate({ 908 pki: [ 909 rvl, 910 obj.config.min, 911 obj.config.max, 912 obj.params.widgetW, 913 obj.params.widgetH, 914 obj.params.dx, 915 obj.params.dy, 916 obj.config.gaugeWidthScale, 917 obj.config.donut, 918 obj.config.reverse 919 ], 920 "fill": color 921 }, obj.config.refreshAnimationTime, obj.config.refreshAnimationType); 922 923 if (obj.config.pointer) { 924 obj.needle.animate({ 925 ndl: [ 926 rvl, 927 obj.config.min, 928 obj.config.max, 929 obj.params.widgetW, 930 obj.params.widgetH, 931 obj.params.dx, 932 obj.params.dy, 933 obj.config.gaugeWidthScale, 934 obj.config.donut 935 ] 936 }, obj.config.refreshAnimationTime, obj.config.refreshAnimationType); 937 } 938 939 // var clear 940 obj, displayVal, color, max = null; 941}; 942 943/** Generate shadow */ 944JustGage.prototype.generateShadow = function(svg, defs) { 945 946 var obj = this; 947 var sid = "inner-shadow-" + obj.config.id;
948 var gaussFilter, feOffset, feGaussianBlur, feComposite1, feFlood, feComposite2, feComposite3; 949 950 // FILTER 951 gaussFilter = document.createElementNS(svg, "filter"); 952 gaussFilter.setAttribute("id", sid); 953 defs.appendChild(gaussFilter); 954 955 // offset 956 feOffset = document.createElementNS(svg, "feOffset"); 957 feOffset.setAttribute("dx", 0); 958 feOffset.setAttribute("dy", obj.config.shadowVerticalOffset); 959 gaussFilter.appendChild(feOffset); 960 961 // blur 962 feGaussianBlur = document.createElementNS(svg, "feGaussianBlur"); 963 feGaussianBlur.setAttribute("result", "offset-blur"); 964 feGaussianBlur.setAttribute("stdDeviation", obj.config.shadowSize); 965 gaussFilter.appendChild(feGaussianBlur); 966 967 // composite 1 968 feComposite1 = document.createElementNS(svg, "feComposite"); 969 feComposite1.setAttribute("operator", "out"); 970 feComposite1.setAttribute("in", "SourceGraphic"); 971 feComposite1.setAttribute("in2", "offset-blur"); 972 feComposite1.setAttribute("result", "inverse"); 973 gaussFilter.appendChild(feComposite1); 974 975 // flood 976 feFlood = document.createElementNS(svg, "feFlood"); 977 feFlood.setAttribute("flood-color", "black"); 978 feFlood.setAttribute("flood-opacity", obj.config.shadowOpacity); 979 feFlood.setAttribute("result", "color"); 980 gaussFilter.appendChild(feFlood); 981 982 // composite 2 983 feComposite2 = document.createElementNS(svg, "feComposite"); 984 feComposite2.setAttribute("operator", "in"); 985 feComposite2.setAttribute("in", "color"); 986 feComposite2.setAttribute("in2", "inverse"); 987 feComposite2.setAttribute("result", "shadow"); 988 gaussFilter.appendChild(feComposite2); 989 990 // composite 3 991 feComposite3 = document.createElementNS(svg, "feComposite"); 992 feComposite3.setAttribute("operator", "over"); 993 feComposite3.setAttribute("in", "shadow"); 994 feComposite3.setAttribute("in2", "SourceGraphic"); 995 gaussFilter.appendChild(feComposite3); 996 997 // set shadow 998 if (!obj.config.hideInnerShadow) { 999 obj.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + sid + ")"); 1000 obj.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + sid + ")"); 1001 } 1002 1003 // var clear 1004 gaussFilter, feOffset, feGaussianBlur, feComposite1, feFlood, feComposite2, feComposite3 = null; 1005}; 1006 1007// 1008// tiny helper function to lookup value of a key from two hash tables 1009// if none found, return defaultvalue 1010// 1011// key: string 1012// tablea: object 1013// tableb: DOMStringMap|object 1014// defval: string|integer|float|null 1015// datatype: return datatype 1016// delimiter: delimiter to be used in conjunction with datatype formatting 1017// 1018function kvLookup(key, tablea, tableb, defval, datatype, delimiter) { 1019 var val = defval; 1020 var canConvert = false; 1021 if (!(key === null || key === undefined)) { 1022 if (tableb !== null && tableb !== undefined && typeof tableb === "object" && key in tableb) { 1023 val = tableb[key]; 1024 canConvert = true; 1025 } else if (tablea !== null && tablea !== undefined && typeof tablea === "object" && key in tablea) { 1026 val = tablea[key]; 1027 canConvert = true; 1028 } else { 1029 val = defval; 1030 } 1031 if (canConvert === true) { 1032 if (datatype !== null && datatype !== undefined) { 1033 switch (datatype) { 1034 case 'int': 1035 val = parseInt(val, 10); 1036 break; 1037 case 'float': 1038 val = parseFloat(val); 1039 break; 1040 default: 1041 break; 1042 } 1043 } 1044 } 1045 } 1046 return val; 1047}; 1048 1049/** Get color for value */ 1050function getColor(val, pct, col, noGradient, custSec) { 1051 1052 var no, inc, colors, percentage, rval, gval, bval, lower, upper, range, rangePct, pctLower, pctUpper, color; 1053 var noGradient = noGradient || custSec.length > 0; 1054 1055 if (custSec.length > 0) { 1056 for (var i = 0; i < custSec.length; i++) { 1057 if (val > custSec[i].lo && val <= custSec[i].hi) { 1058 return custSec[i].color; 1059 } 1060 } 1061 } 1062 1063 no = col.length; 1064 if (no === 1) return col[0]; 1065 inc = (noGradient) ? (1 / no) : (1 / (no - 1)); 1066 colors = []; 1067 for (i = 0; i < col.length; i++) { 1068 percentage = (noGradient) ? (inc * (i + 1)) : (inc * i); 1069 rval = parseInt((cutHex(col[i])).substring(0, 2), 16); 1070 gval = parseInt((cutHex(col[i])).substring(2, 4), 16); 1071 bval = parseInt((cutHex(col[i])).substring(4, 6), 16); 1072 colors[i] = { 1073 pct: percentage, 1074 color: { 1075 r: rval, 1076 g: gval, 1077 b: bval 1078 } 1079 }; 1080 } 1081 1082 if (pct === 0) { 1083 return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')'; 1084 } 1085 1086 for (var j = 0; j < colors.length; j++) { 1087 if (pct <= colors[j].pct) { 1088 if (noGradient) { 1089 return 'rgb(' + [colors[j].color.r, colors[j].color.g, colors[j].color.b].join(',') + ')'; 1090 } else { 1091 lower = colors[j - 1]; 1092 upper = colors[j]; 1093 range = upper.pct - lower.pct; 1094 rangePct = (pct - lower.pct) / range; 1095 pctLower = 1 - rangePct; 1096 pctUpper = rangePct; 1097 color = { 1098 r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper), 1099 g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper), 1100 b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper) 1101 }; 1102 return 'rgb(' + [color.r, color.g, color.b].join(',') + ')'; 1103 } 1104 } 1105 } 1106 1107} 1108 1109/** Fix Raphael display:none tspan dy attribute bug */ 1110function setDy(elem, fontSize, txtYpos) { 1111 if ((!ie || ie > 9) && elem.node.firstChild.attributes.dy) { 1112 elem.node.firstChild.attributes.dy.value = 0; 1113 } 1114} 1115 1116/** Random integer */ 1117function getRandomInt(min, max) { 1118 return Math.floor(Math.random() * (max - min + 1)) + min; 1119} 1120 1121/** Cut hex */ 1122function cutHex(str) { 1123 return (str.charAt(0) == "#") ? str.substring(1, 7) : str; 1124} 1125 1126/** Human friendly number suffix - From: http://stackoverflow.com/questions/2692323/code-golf-friendly-number-abbreviator */ 1127function humanFriendlyNumber(n, d) { 1128 var p, d2, i, s; 1129 1130 p = Math.pow; 1131 d2 = p(10, d); 1132 i = 7; 1133 while (i) { 1134 s = p(10, i-- * 3); 1135 if (s <= n) { 1136 n = Math.round(n * d2 / s) / d2 + "KMGTPE" [i]; 1137 } 1138 } 1139 return n; 1140} 1141 1142/** Format numbers with commas - From: http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript */ 1143function formatNumber(x) { 1144 var parts = x.toString().split("."); 1145 parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); 1146 return parts.join("."); 1147} 1148 1149/** Get style */ 1150function getStyle(oElm, strCssRule) { 1151 var strValue = ""; 1152 if (document.defaultView && document.defaultView.getComputedStyle) { 1153 strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); 1154 } else if (oElm.currentStyle) { 1155 strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1) { 1156 return p1.toUpperCase(); 1157 });
1158 strValue = oElm.currentStyle[strCssRule]; 1159 } 1160 return strValue; 1161} 1162 1163/** Create Element NS Ready */ 1164function onCreateElementNsReady(func) { 1165 if (document.createElementNS !== undefined) { 1166 func(); 1167 } else { 1168 setTimeout(function() { 1169 onCreateElementNsReady(func); 1170 }, 100); 1171 } 1172} 1173 1174/** Get IE version */ 1175// ---------------------------------------------------------- 1176// A short snippet for detecting versions of IE in JavaScript 1177// without resorting to user-agent sniffing 1178// ---------------------------------------------------------- 1179// If you're not in IE (or IE version is less than 5) then: 1180// ie === undefined 1181// If you're in IE (>=5) then you can determine which version: 1182// ie === 7; // IE7 1183// Thus, to detect IE: 1184// if (ie) {} 1185// And to detect the version: 1186// ie === 6 // IE6 1187// ie > 7 // IE8, IE9 ... 1188// ie < 9 // Anything less than IE9 1189// ---------------------------------------------------------- 1190// UPDATE: Now using Live NodeList idea from @jdalton 1191var ie = (function() { 1192 1193 var undef, 1194 v = 3, 1195 div = document.createElement('div'), 1196 all = div.getElementsByTagName('i'); 1197 1198 while ( 1199 div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', 1200 all[0] 1201 ); 1202 return v > 4 ? v : undef; 1203}()); 1204 1205// extend target object with second object 1206function extend(out) { 1207 out = out || {}; 1208 1209 for (var i = 1; i < arguments.length; i++) { 1210 if (!arguments[i]) 1211 continue; 1212 1213 for (var key in arguments[i]) { 1214 if (arguments[i].hasOwnProperty(key)) 1215 out[key] = arguments[i][key]; 1216 } 1217 } 1218 1219 return out; 1220};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.