PageSourceSearch

https://www.parliament.go.th/assets/theme/Main_theme/files/js/justgage.js?v=260924035033

js parliament.go.th collected 2026-09-24 08:50:40 UTC 39,335 bytes, 1,220 lines download raw bytes

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.