ad881c3eb894a34bbdbb54908487503a648de921
[osm/UI.git] / skyquake / framework / js / gauge-modified.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 // (function(window){
20 /**!
21 * @license
22 * HTML5 Canvas Gauge implementation
23 *
24 * This code is subject to MIT license.
25 *
26 * Copyright (c) 2012 Mykhailo Stadnyk <mikhus@gmail.com>
27 *
28 * Permission is hereby granted, free of charge, to any person obtaining a copy of
29 * this software and associated documentation files (the "Software"), to deal in
30 * the Software without restriction, including without limitation the rights to use,
31 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
32 * Software, and to permit persons to whom the Software is furnished to do so,
33 * subject to the following conditions:
34 *
35 * The above copyright notice and this permission notice shall be included in all
36 * copies or substantial portions of the Software.
37 *
38 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
40 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
41 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
42 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 *
45 * @authors: Mykhailo Stadnyk <mikhus@gmail.com>
46 * Chris Poile <poile@edwards.usask.ca>
47 * Luca Invernizzi <http://www.lucainvernizzi.net>
48 * Robert Blackburn <http://www.rwblackburn.com>
49 */
50
51 /**
52 * @param {Object} config
53 * @constructor
54 */
55 var Gauge = function (config) {
56 Gauge.Collection.push(this);
57
58 /**
59 * Default gauge configuration
60 * @struct
61 */
62 this.config = {
63 isAggregate: false,
64 renderTo: null,
65 width: 200,
66 height: 200,
67 title: false,
68 maxValue: 100,
69 minValue: 0,
70 majorTicks: [],
71 minorTicks: 10,
72 strokeTicks: true,
73 units: false,
74 valueFormat: { "int": 3, "dec": 2 },
75 majorTicksFormat: { "int": 1, "dec": 0 },
76 glow: true,
77 animation: {
78 delay: 10,
79 duration: 250,
80 fn: 'cycle'
81 },
82 colors: {
83 plate: '#fff',
84 majorTicks: '#444',
85 minorTicks: '#666',
86 title: '#888',
87 units: '#888',
88 numbers: '#444',
89 needle: { start: 'rgba(240, 128, 128, 1)', end: 'rgba(255, 160, 122, .9)' }
90 },
91 highlights: [
92 {
93 from: 20,
94 to: 60,
95 color: '#eee'
96 },
97 {
98 from: 60,
99 to: 80,
100 color: '#ccc'
101 },
102 {
103 from: 80,
104 to: 100,
105 color: '#999'
106 }
107 ]
108 };
109
110 var
111 value = 0,
112 self = this,
113 fromValue = 0,
114 toValue = 0,
115 imready = false
116 ;
117
118 /**
119 * Sets a new value to gauge and updates the gauge view
120 *
121 * @param {number} val - the new value to set to the gauge
122 * @return {Gauge} this - returns self
123 */
124 this.setValue = function (val) {
125
126 fromValue = config.animation ? value : val;
127
128 var dv = (config.maxValue - config.minValue) / 100;
129
130 toValue = val > config.maxValue ?
131 config.maxValue + dv :
132 val < config.minValue ?
133 config.minValue - dv :
134 val
135 ;
136
137 value = val;
138
139 if (toValue >= fromValue) {
140 config.arrow = 'up';
141 } else {
142 config.arrow = 'down';
143 }
144
145 config.animation ? animate() : this.draw();
146
147 return this;
148 };
149
150 /**
151 * Sets a new value to gauge and updates the gauge view without
152 * any animation (even if configured)
153 *
154 * @param {number} val - the new value to set to the gauge
155 * @return {Gauge} this - returns self
156 */
157 this.setRawValue = function (val) {
158 fromValue = value = val;
159 this.draw();
160 return this;
161 };
162
163 /**
164 * Clears the value of the gauge
165 * @return {Gauge}
166 */
167 this.clear = function () {
168 value = fromValue = toValue = this.config.minValue;
169 this.draw();
170 return this;
171 };
172
173
174 /**
175 * Returns the current value been set to the gauge
176 *
177 * @return {number} value - current gauge's value
178 */
179 this.getValue = function () {
180 return value;
181 };
182
183 /**
184 * Ready event for the gauge. Use it whenever you
185 * initialize the gauge to be assured it was fully drawn
186 * before you start the update on it
187 *
188 * @event {Function} onready
189 */
190 this.onready = function () {
191 };
192
193 function applyRecursive(dst, src) {
194 for (var i in src) {
195 // modification by Chris Poile, Oct 08, 2012. More correct check of an Array instance
196 if (typeof src[i] == "object" && !(Object.prototype.toString.call(src[i]) === '[object Array]') && i != 'renderTo') {
197 if (typeof dst[i] != "object") {
198 dst[i] = {};
199 }
200
201 applyRecursive(dst[i], src[i]);
202 } else {
203 dst[i] = src[i];
204 }
205 }
206 };
207
208 applyRecursive(this.config, config);
209
210 this.config.minValue = parseFloat(this.config.minValue);
211 this.config.maxValue = parseFloat(this.config.maxValue);
212
213 config = this.config;
214 fromValue = value = config.minValue;
215
216 if (!config.renderTo) {
217 throw Error("Canvas element was not specified when creating the Gauge object!");
218 }
219
220 var
221 canvas = config.renderTo.tagName ? config.renderTo : document.getElementById(config.renderTo),
222 ctx = canvas.getContext('2d'),
223 cache, CW, CH, CX, CY, max, cctx
224 ;
225
226 function baseInit() {
227 canvas.width = config.width;
228 canvas.height = config.height;
229
230 cache = canvas.cloneNode(true);
231 cctx = cache.getContext('2d');
232 CW = canvas.width;
233 CH = canvas.height;
234 CX = CW / 2;
235 CY = CH / 2;
236 max = CX < CY ? CX : CY;
237
238 cache.i8d = false;
239
240 // translate cache to have 0, 0 in center
241 cctx.translate(CX, CY);
242 cctx.save();
243
244 // translate canvas to have 0,0 in center
245 ctx.translate(CX, CY);
246 ctx.save();
247 };
248
249 // do basic initialization
250 baseInit();
251
252 /**
253 * Updates the gauge config
254 *
255 * @param {Object} config
256 * @return {Gauge}
257 */
258 this.updateConfig = function (config) {
259 applyRecursive(this.config, config);
260 baseInit();
261 this.draw();
262 return this;
263 };
264
265 var animateFx = {
266 linear: function (p) {
267 return p;
268 },
269 quad: function (p) {
270 return Math.pow(p, 2);
271 },
272 quint: function (p) {
273 return Math.pow(p, 5);
274 },
275 cycle: function (p) {
276 return 1 - Math.sin(Math.acos(p));
277 },
278 bounce: function (p) {
279 return 1 - (function (p) {
280 for (var a = 0, b = 1; 1; a += b, b /= 2) {
281 if (p >= (7 - 4 * a) / 11) {
282 return -Math.pow((11 - 6 * a - 11 * p) / 4, 2) + Math.pow(b, 2);
283 }
284 }
285 })(1 - p);
286 },
287 elastic: function (p) {
288 return 1 - (function (p) {
289 var x = 1.5;
290 return Math.pow(2, 10 * (p - 1)) * Math.cos(20 * Math.PI * x / 3 * p);
291 })(1 - p);
292 }
293 };
294
295 var animateInterval = null;
296
297 function _animate(opts) {
298 var start = new Date;
299
300 animateInterval = setInterval(function () {
301 var
302 timePassed = new Date - start,
303 progress = timePassed / opts.duration
304 ;
305
306 if (progress > 1) {
307 progress = 1;
308 }
309
310 var animateFn = typeof opts.delta == "function" ?
311 opts.delta :
312 animateFx[opts.delta]
313 ;
314
315 var delta = animateFn(progress);
316 opts.step(delta);
317
318 if (progress == 1) {
319 clearInterval(animateInterval);
320 }
321 }, opts.delay || 10);
322 };
323
324 function animate() {
325 animateInterval && clearInterval(animateInterval); // stop previous animation
326 var
327 path = (toValue - fromValue),
328 from = fromValue,
329 cfg = config.animation
330 ;
331
332 _animate({
333 delay: cfg.delay,
334 duration: cfg.duration,
335 delta: cfg.fn,
336 step: function (delta) {
337 fromValue = parseFloat(from) + path * delta;
338 self.draw();
339 }
340 });
341 };
342
343 // defaults
344 ctx.lineCap = "round";
345
346 /**
347 * Drows the gauge. Normally this function should be used to
348 * initally draw the gauge
349 *
350 * @return {Gauge} this - returns the self Gauge object
351 */
352 this.draw = function () {
353 if (!cache.i8d) {
354 // clear the cache
355 cctx.clearRect(-CX, -CY, CW, CH);
356 cctx.save();
357
358 var tmp = {ctx: ctx};
359 ctx = cctx;
360
361 drawPlate();
362 drawHighlights();
363 drawMinorTicks();
364 drawMajorTicks();
365 drawNumbers();
366 drawTitle();
367 drawUnits();
368
369 cache.i8d = true;
370 ctx = tmp.ctx;
371 delete tmp.ctx;
372 }
373
374 // clear the canvas
375 ctx.clearRect(-CX, -CY, CW, CH);
376 ctx.save();
377
378 ctx.drawImage(cache, -CX, -CY, CW, CH);
379
380 if (!Gauge.initialized) {
381 var iv = setInterval(function () {
382 if (!Gauge.initialized) {
383 return;
384 }
385
386 clearInterval(iv);
387
388 drawValueBox();
389 drawNeedle();
390 // drawArrow();
391
392 if (!imready) {
393 self.onready && self.onready();
394 imready = true;
395 }
396 }, 10);
397 } else {
398 drawValueBox();
399 drawNeedle();
400 // drawArrow();
401
402 if (!imready) {
403 self.onready && self.onready();
404 imready = true;
405 }
406 }
407
408 return this;
409 };
410
411 /**
412 * Transforms degrees to radians
413 */
414 function radians(degrees) {
415 return degrees * Math.PI / 180;
416 };
417
418 /**
419 * Linear gradient
420 */
421 function lgrad(clrFrom, clrTo, len) {
422 var grad = ctx.createLinearGradient(0, 0, 0, len);
423 grad.addColorStop(0, clrFrom);
424 grad.addColorStop(1, clrTo);
425
426 return grad;
427 };
428
429 function drawPlate() {
430 var
431 r0 = max / 100 * 93,
432 d0 = max - r0,
433 r1 = max / 100 * 91,
434 d1 = max - r1,
435 r2 = max / 100 * 88,
436 d2 = max - r2,
437 r3 = max / 100 * 85;
438
439 ctx.save();
440
441 if (config.glow) {
442 ctx.shadowBlur = d0;
443 ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
444 }
445
446 ctx.beginPath();
447 ctx.arc(0, 0, r0, 0, Math.PI * 2, true);
448 // ctx.fillStyle = lgrad( '#ddd', '#aaa', r0);
449 ctx.fillStyle = lgrad('hsla(0, 0%, 17%, 1)', 'hsla(0, 0%, 0%, 1)', r0);
450 //ctx.fill();
451
452 //ctx.restore();
453
454 ctx.beginPath();
455 ctx.arc(0, 0, r1, 0, Math.PI * 2, true);
456 // ctx.fillStyle = lgrad( '#fafafa', '#ccc', r1);
457 ctx.fillStyle = lgrad('hsla(0, 0%, 47%, 1)', 'hsla(0, 0%, 33%, 1)', r1);
458 //ctx.fill();
459
460 ctx.beginPath();
461 ctx.arc(0, 0, r2, 0, Math.PI * 2, true);
462 // ctx.fillStyle = lgrad( '#eee', '#f0f0f0', r2);
463 ctx.fillStyle = lgrad('hsla(0, 0%, 33%, 1)', 'hsla(0, 0%, 43%, 1)', r2);
464 //ctx.fill();
465
466 ctx.beginPath();
467 ctx.arc(0, 0, r3, 0, Math.PI * 2, true);
468 ctx.fillStyle = config.colors.plate;
469 //ctx.fill();
470
471 ctx.save();
472 };
473
474 /**
475 * Formats a number for display on the dial's plate using the majorTicksFormat config option.
476 *
477 * @param {number} num The number to format
478 * @returns {string} The formatted number
479 */
480 function formatMajorTickNumber(num) {
481 var r, isDec = false;
482
483 // First, force the correct number of digits right of the decimal.
484 if (config.majorTicksFormat.dec === 0) {
485 r = Math.round(num).toString();
486 } else {
487 r = num.toFixed(config.majorTicksFormat.dec);
488 }
489
490 // Second, force the correct number of digits left of the decimal.
491 if (config.majorTicksFormat["int"] > 1) {
492 // Does this number have a decimal?
493 isDec = (r.indexOf('.') > -1);
494
495 // Is this number a negative number?
496 if (r.indexOf('-') > -1) {
497 return '-' + [
498 config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 2 + (isDec ? 1 : 0) - r.length
499 ].join('0') + r.replace('-', '');
500 } else {
501 return [
502 config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 1 + (isDec ? 1 : 0) - r.length
503 ].join('0') + r;
504 }
505 } else {
506 return r;
507 }
508 }
509
510 // major ticks draw
511 function drawMajorTicks() {
512 var r = max / 100 * 81;
513
514 ctx.lineWidth = 1;
515 ctx.strokeStyle = config.colors.majorTicks;
516 ctx.save();
517
518 if (config.majorTicks.length === 0) {
519 var numberOfDefaultTicks = 5;
520 var tickSize = (config.maxValue - config.minValue) / numberOfDefaultTicks;
521
522 for (var i = 0; i < numberOfDefaultTicks; i++) {
523 config.majorTicks.push(formatMajorTickNumber(config.minValue + (tickSize * i)));
524 }
525 config.majorTicks.push(formatMajorTickNumber(config.maxValue));
526 }
527
528 for (var i = 0; i < config.majorTicks.length; ++i) {
529 var a = 45 + i * (270 / (config.majorTicks.length - 1));
530 ctx.rotate(radians(a));
531
532 ctx.beginPath();
533 ctx.moveTo(0, r);
534 ctx.lineTo(0, r - max / 100 * 15);
535 ctx.stroke();
536
537 ctx.restore();
538 ctx.save();
539 }
540
541 if (config.strokeTicks) {
542 ctx.rotate(radians(90));
543
544 ctx.beginPath();
545 ctx.arc(0, 0, r, radians(45), radians(315), false);
546 ctx.stroke();
547 ctx.restore();
548
549 ctx.save();
550 }
551 };
552
553 // minor ticks draw
554 function drawMinorTicks() {
555 var r = max / 100 * 81;
556
557 ctx.lineWidth = 1;
558 ctx.strokeStyle = config.colors.minorTicks;
559
560 ctx.save();
561
562 var len = config.minorTicks * (config.majorTicks.length - 1);
563
564 for (var i = 0; i < len; ++i) {
565 var a = 45 + i * (270 / len);
566 ctx.rotate(radians(a));
567
568 ctx.beginPath();
569 ctx.moveTo(0, r);
570 ctx.lineTo(0, r - max / 100 * 7.5);
571 ctx.stroke();
572
573 ctx.restore();
574 ctx.save();
575 }
576 };
577
578 // tick numbers draw
579 function drawNumbers() {
580 //var r = max / 100 * 55;
581 //
582 //for (var i = 0; i < config.majorTicks.length; ++i) {
583 // var
584 // a = 45 + i * (270 / (config.majorTicks.length - 1)),
585 // p = rpoint(r, radians(a))
586 // ;
587 //
588 // ctx.font = 20 * (max / 200) + "px Arial";
589 // ctx.fillStyle = config.colors.numbers;
590 // ctx.lineWidth = 0;
591 // ctx.textAlign = "center";
592 // ctx.fillText(config.majorTicks[i], p.x, p.y + 3);
593 //}
594 };
595
596 // title draw
597 function drawTitle() {
598 if (!config.title) {
599 return;
600 }
601
602 ctx.save();
603 ctx.font = 24 * (max / 200) + "px Arial";
604 ctx.fillStyle = config.colors.title;
605 ctx.textAlign = "center";
606 ctx.fillText(config.title, 0, -max / 4.25);
607 ctx.restore();
608 };
609
610 // units draw
611 function drawUnits() {
612 if (!config.units) {
613 return;
614 }
615
616 ctx.save();
617 ctx.font = 22 * (max / 200) + "px Arial";
618 ctx.fillStyle = config.colors.units;
619 ctx.textAlign = "center";
620 ctx.fillText(config.units, 0, max / 3.25);
621 ctx.restore();
622 };
623
624
625 function drawArrow() {
626
627 if (config.arrow != "false") {
628
629 if (config.arrow == "up") {
630 var r = max / 100 * 2.0;
631 y0 = max / 100 * 64;
632 y1 = max / 100 * 55;
633 y2 = max / 100 * 53;
634 y3 = max / 100 * 50;
635 y4 = max / 100 * 45;
636 arrow_color = "rgba(107, 184, 20, 1)";
637
638 var x0 = max / 100 * -8
639 var x1 = max / 100 * -6
640 var x2 = max / 100 * -1.5
641 var x3 = max / 100 * 0
642 var x4 = max / 100 * 1.5
643 var x5 = max / 100 * 6
644 var x6 = max / 100 * 8
645
646 ctx.beginPath();
647 ctx.moveTo(x0, y2);
648 ctx.lineTo(x3 - r, y4 + r);
649 ctx.arcTo(x3, y4 - r, x3 + r, y4 + r, r * 1.09);
650 ctx.lineTo(x6, y2);
651 ctx.arcTo(x6 + r/2.0, y2 + r/1.0, x5, y1, r*.9)
652 ctx.lineTo(x4, y3);
653 ctx.lineTo(x4, y0);
654 ctx.arcTo(x3, y0 + r, x2, y0, r*.9);
655 ctx.lineTo(x2, y3);
656 ctx.lineTo(x1, y1);
657 ctx.arcTo(x1 - r, y1 - r/2.0, x0, y2, r*1.09)
658
659
660 ctx.closePath();
661 ctx.fillStyle = arrow_color;
662 ctx.fill();
663 } else {
664 var r = max / 100 * 2.0;
665 var y0 = max / 100 * 45;
666 var y1 = max / 100 * 54;
667 var y2 = max / 100 * 56;
668 var y3 = max / 100 * 59;
669 var y4 = max / 100 * 64;
670 var arrow_color = "rgba(252, 38, 50, 1)";
671
672 var x0 = max / 100 * -8
673 var x1 = max / 100 * -6
674 var x2 = max / 100 * -1.5
675 var x3 = max / 100 * 0
676 var x4 = max / 100 * 1.5
677 var x5 = max / 100 * 6
678 var x6 = max / 100 * 8
679
680 ctx.beginPath();
681 ctx.moveTo(x0, y2);
682 ctx.lineTo(x3 - r, y4 - r);
683 ctx.arcTo(x3, y4 + r, x3 + r, y4 - r, r * 1.09);
684 ctx.lineTo(x6, y2);
685 ctx.arcTo(x6 + r/2.0, y2 - r/1.0, x5, y1, r*.9)
686 ctx.lineTo(x4, y3);
687 ctx.lineTo(x4, y0);
688 ctx.arcTo(x3, y0 - r, x2, y0, r*.9);
689 ctx.lineTo(x2, y3);
690 ctx.lineTo(x1, y1);
691 ctx.arcTo(x1 - r, y1 + r/2.0, x0, y2, r*1.09)
692
693
694 ctx.closePath();
695 ctx.fillStyle = arrow_color;
696 ctx.fill();
697 }
698 ctx.save();
699 ctx.restore();
700 }
701 }
702
703 function padValue(val) {
704 var cdec = config.valueFormat['dec']
705 var cint = config.valueFormat['int']
706
707 val = parseFloat(val);
708 var n = (val < 0);
709
710 val = Math.abs(val);
711
712 if (cdec > 0) {
713 val = val.toFixed(cdec).toString().split('.');
714
715 for (var i = 0, s = cint - val[0].length; i < s; ++i) {
716 val[0] = '0' + val[0];
717 }
718
719 val = (n ? '-' : '') + val[0] + '.' + val[1];
720 } else {
721 val = Math.round(val).toString();
722
723 for (var i = 0, s = cint - val.length; i < s; ++i) {
724 val = '0' + val;
725 }
726
727 val = (n ? '-' : '') + val
728 }
729
730 return val;
731 };
732
733 function rpoint(r, a) {
734 var
735 x = 0, y = r,
736
737 sin = Math.sin(a),
738 cos = Math.cos(a),
739
740 X = x * cos - y * sin,
741 Y = x * sin + y * cos
742 ;
743
744 return { x: X, y: Y };
745 };
746 function clearCircle(x, y, radius)
747 {
748 ctx.beginPath();
749 ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
750 ctx.clip();
751 ctx.clearRect(x - radius - 1, y - radius - 1,
752 radius * 2 + 2, radius * 2 + 2);
753
754 };
755 // draws the highlight colors
756 function drawHighlights() {
757 ctx.save();
758
759 var r1 = max / 100 * 81;
760 var r2 = r1 - max / 100 * 15;
761
762 for (var i = 0, s = config.highlights.length; i < s; i++) {
763 var
764 hlt = config.highlights[i],
765 vd = (config.maxValue - config.minValue) / 270,
766 sa = radians(45 + (hlt.from - config.minValue) / vd),
767 ea = radians(45 + (hlt.to - config.minValue) / vd)
768 ;
769
770 ctx.beginPath();
771
772 ctx.rotate(radians(90));
773 ctx.arc(0, 0, r1, sa, ea, false);
774 ctx.restore();
775 ctx.save();
776
777 var
778 ps = rpoint(r2, sa),
779 pe = rpoint(r1, sa)
780 ;
781 ctx.moveTo(ps.x, ps.y);
782 ctx.lineTo(pe.x, pe.y);
783
784 var
785 ps1 = rpoint(r1, ea),
786 pe1 = rpoint(r2, ea)
787 ;
788
789 ctx.lineTo(ps1.x, ps1.y);
790 ctx.lineTo(pe1.x, pe1.y);
791 ctx.lineTo(ps.x, ps.y);
792
793 ctx.closePath();
794
795 ctx.fillStyle = hlt.color;
796 ctx.fill();
797
798 ctx.beginPath();
799 ctx.rotate(radians(90));
800 ctx.arc(0, 0, r2, sa - 0.2, ea + 0.2, false);
801 ctx.restore();
802
803 ctx.closePath();
804
805 ctx.fillStyle = config.colors.plate;
806 ctx.fill();
807 ctx.save();
808 ctx.imageSmoothingEnabled = true
809 //clearCircle(0, 0, 100)
810
811 }
812 };
813
814 // drows the gauge needle
815 function drawNeedle() {
816 var
817 r1 = 0 ,
818 r2 = 0 ,
819 rIn = max / 100 * 85,
820 rOut = max / 100 * 63,
821 rP = max / 100 * 59,
822 pad1 = max / 100 * 3,
823 pad2 = max / 100 * 2.5,
824
825 shad = function () {
826 ctx.shadowOffsetX = 2;
827 ctx.shadowOffsetY = 2;
828 ctx.shadowBlur = 10;
829 // ctx.shadowColor = 'rgba(188, 143, 143, 0.45)';
830 ctx.shadowColor = 'rgba(50, 50, 50, .3)';
831 }
832 ;
833
834 shad();
835
836 ctx.save();
837
838 if (fromValue < 0) {
839 fromValue = Math.abs(config.minValue - fromValue);
840 } else if (config.minValue > 0) {
841 fromValue -= config.minValue
842 } else {
843 fromValue = Math.abs(config.minValue) + fromValue;
844 }
845
846 ctx.rotate(radians(45 + fromValue / ((config.maxValue - config.minValue) / 270)));
847
848 ctx.beginPath();
849 ctx.lineTo(-pad2, rOut);
850 ctx.lineTo(-pad2, rIn);
851 ctx.lineTo(pad2, rIn);
852 ctx.lineTo(pad2, rOut);
853 ctx.lineTo(0, rP);
854 ctx.closePath();
855 ctx.strokeStyle = "#999"
856 ctx.stroke();
857
858 ctx.fillStyle = lgrad(
859 config.colors.needle.start,
860 config.colors.needle.end,
861 rIn - rOut
862 );
863 ctx.fill();
864
865 // ctx.beginPath();
866 // ctx.lineTo(-_pad2, _rOut);
867 // ctx.lineTo(-_pad2, _rIn);
868 // ctx.lineTo(_pad2, _rIn);
869 // ctx.lineTo(_pad2, _rOut);
870 // ctx.lineTo(0, _rOut - 5);
871 // ctx.closePath();
872
873 // ctx.fillStyle = "#ccc"
874 // ctx.fill();
875
876 ctx.beginPath();
877 ctx.lineTo(-pad2, rIn);
878 ctx.lineTo(-pad2, rIn);
879 ctx.lineTo(-pad1, 0);
880 ctx.lineTo(-pad2, rOut);
881 ctx.lineTo(pad2 / 2 - 2, rOut);
882 ctx.closePath();
883 ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
884
885 //ctx.fill();
886
887 ctx.restore();
888
889 //shad();
890
891 ctx.beginPath();
892 ctx.arc(0, 0, r2 +.5, 0, Math.PI * 2, true);
893 // ctx.fillStyle = lgrad( '#f0f0f0', '#ccc', r1);
894 ctx.fillStyle = lgrad('#3b3b3b', '#121212', r1);
895 //ctx.fill();
896
897 ctx.restore();
898
899 ctx.beginPath();
900 ctx.arc(0, 0, r2, 0, Math.PI * 2, true);
901 // ctx.fillStyle = lgrad( "#e8e8e8", "#f5f5f5", r2);
902 ctx.fillStyle = 'rgba(255,255,255,1)';
903 //ctx.fill();
904 };
905
906 function roundRect(x, y, w, h, r) {
907 ctx.beginPath();
908
909 ctx.moveTo(x + r, y);
910 ctx.lineTo(x + w - r, y);
911
912 ctx.quadraticCurveTo(x + w, y, x + w, y + r);
913 ctx.lineTo(x + w, y + h - r);
914
915 ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
916 ctx.lineTo(x + r, y + h);
917
918 ctx.quadraticCurveTo(x, y + h, x, y + h - r);
919 ctx.lineTo(x, y + r);
920
921 ctx.quadraticCurveTo(x, y, x + r, y);
922
923 ctx.closePath();
924 };
925
926 // value box draw
927 function drawValueBox() {
928 ctx.save();
929
930 ctx.font = 100 + " " + 73 * (max / 200) + "px 'roboto-light";
931 var
932 text = padValue(value),
933 tw = ctx.measureText('-' + padValue(0)).width,
934 y = max - max / 100 * 96,
935 x = 0,
936 th = 0.12 * max
937 ;
938
939 ctx.save();
940
941 roundRect(
942 -tw / 2 - 0.025 * max,
943 y - th - 0.04 * max,
944 tw + 0.05 * max,
945 th + 0.07 * max,
946 0.025 * max
947 );
948
949 var grd = ctx.createRadialGradient(
950 x,
951 y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2,
952 max / 10,
953 x,
954 y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2,
955 max / 5
956 );
957
958 // grd.addColorStop( 0, "#888");
959 // grd.addColorStop( 1, "#666");
960
961 // ctx.strokeStyle = grd;
962 // ctx.lineWidth = 0.05 * max;
963 // ctx.stroke();
964
965 // ctx.shadowBlur = 0.012 * max;
966 // ctx.shadowColor = 'rgba(0, 0, 0, 1)';
967
968 // ctx.fillStyle = "#babab2";
969 // ctx.fill();
970
971 // ctx.restore();
972
973 // ctx.shadowOffsetX = 0.004 * max;
974 // ctx.shadowOffsetY = 0.004 * max;
975 // ctx.shadowBlur = 0.012 * max;
976 // ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
977
978 // ctx.fillStyle = "#444";
979 ctx.fillStyle = "rgba(50,50,50,1)";
980 // ctx.fillStyle = "rgba(50,50,50,1)";
981 ctx.textAlign = "center";
982
983 ctx.fillText(text, -x, y);
984
985
986 ctx.save();
987 ctx.font = 100 + " " + 20 * (max / 200) + "px 'roboto-light";
988 //ctx.fillText(config.unit, -x, y + 30);
989 ctx.restore();
990
991 };
992 };
993
994 // initialize
995 Gauge.initialized = false;
996 (function () {
997 var
998 d = document,
999 h = d.getElementsByTagName('head')[0],
1000 ie = navigator.userAgent.toLocaleLowerCase().indexOf('msie') != -1,
1001 url = 'fonts/digital-7-mono.' + (ie ? 'eot' : 'ttf'),
1002
1003 // RW: don't use mono font, this was causing err in js console
1004 text = '',
1005 // text = "@font-face {" +
1006 // "font-family: 'Led';" +
1007 // "src: url('" + url + "');" +
1008 // "}",
1009 ss,
1010 r = d.createElement('style')
1011 ;
1012
1013 r.type = 'text/css';
1014
1015 if (ie) {
1016 h.appendChild(r);
1017 ss = r.styleSheet;
1018 ss.cssText = text;
1019 } else {
1020 try {
1021 r.appendChild(d.createTextNode(text));
1022 } catch (e) {
1023 r.cssText = text;
1024 }
1025
1026 h.appendChild(r);
1027
1028 ss = r.styleSheet ? r.styleSheet :
1029 (r.sheet || d.styleSheets[d.styleSheets.length - 1])
1030 ;
1031 }
1032
1033 var iv = setInterval(function () {
1034 if (!d.body) {
1035 return;
1036 }
1037
1038 clearInterval(iv);
1039
1040 var dd = d.createElement('div');
1041
1042 dd.style.fontFamily = 'Led';
1043 dd.style.position = 'absolute';
1044 dd.style.height = dd.style.width = 0;
1045 dd.style.overflow = 'hidden';
1046
1047 dd.innerHTML = '.';
1048
1049 d.body.appendChild(dd);
1050
1051 setTimeout(function () { // no other way to handle font is rendered by a browser
1052 // just give the browser around 250ms to do that :(
1053 Gauge.initialized = true;
1054 dd.parentNode.removeChild(dd);
1055 }, 250);
1056 }, 1);
1057 })();
1058
1059 Gauge.Collection = [];
1060 Gauge.Collection.get = function (id) {
1061 var self = this;
1062
1063 if (typeof(id) == 'string') {
1064 for (var i = 0, s = self.length; i < s; i++) {
1065 var canvas = self[i].config.renderTo.tagName ? self[i].config.renderTo : document.getElementById(self[i].config.renderTo);
1066 if (canvas.getAttribute('id') == id) {
1067 return self[i];
1068 }
1069 }
1070 } else if (typeof(id) == 'number') {
1071 return self[id];
1072 } else {
1073 return null;
1074 }
1075 };
1076
1077 function domReady(handler) {
1078 if (window.addEventListener) {
1079 window.addEventListener('DOMContentLoaded', handler, false);
1080 } else {
1081 window.attachEvent('onload', handler);
1082 }
1083 }
1084
1085 domReady(function () {
1086 function toCamelCase(arr) {
1087 var str = arr[0];
1088 for (var i = 1, s = arr.length; i < s; i++) {
1089 str += arr[i].substr(0, 1).toUpperCase() + arr[i].substr(1, arr[i].length - 1);
1090 }
1091 return str;
1092 };
1093
1094 function trim(str) {
1095 return str.replace(/^\s+|\s+$/g, '');
1096 };
1097
1098 var c = document.getElementsByTagName('canvas');
1099
1100 for (var i = 0, s = c.length; i < s; i++) {
1101
1102 if (c[i].getAttribute('data-type') == 'canv-gauge') {
1103 var
1104 gauge = c[i],
1105 config = {},
1106 prop,
1107 w = parseInt(gauge.getAttribute('width'), 10),
1108 h = parseInt(gauge.getAttribute('height'), 10)
1109 ;
1110
1111 config.renderTo = gauge;
1112
1113 if (w) {
1114 config.width = w;
1115 }
1116
1117 if (h) {
1118 config.height = h;
1119 }
1120
1121 for (var ii = 0, ss = gauge.attributes.length; ii < ss; ii++) {
1122 prop = gauge.attributes.item(ii).nodeName;
1123
1124 if (prop != 'data-type' && prop.substr(0, 5) == 'data-') {
1125 var
1126 cfgProp = prop.substr(5, prop.length - 5).toLowerCase().split('-'),
1127 attrValue = gauge.getAttribute(prop)
1128 ;
1129
1130 if (!attrValue) {
1131 continue;
1132 }
1133
1134 switch (cfgProp[0]) {
1135 case 'colors' :
1136 {
1137 if (cfgProp[1]) {
1138 if (!config.colors) {
1139 config.colors = {};
1140 }
1141
1142 if (cfgProp[1] == 'needle') {
1143 var parts = attrValue.split(/\s+/);
1144
1145 if (parts[0] && parts[1]) {
1146 config.colors.needle = { start: parts[0], end: parts[1] };
1147 }
1148 else {
1149 config.colors.needle = attrValue;
1150 }
1151 }
1152 else {
1153 cfgProp.shift();
1154 config.colors[toCamelCase(cfgProp)] = attrValue;
1155 }
1156 }
1157 break;
1158 }
1159 case 'highlights' :
1160 {
1161 if (!config.highlights) {
1162 config.highlights = [];
1163 }
1164
1165 var hls = attrValue.match(/(?:(?:-?\d*\.)?(-?\d+){1,2} ){2}(?:(?:#|0x)?(?:[0-9A-F|a-f]){3,8}|rgba?\(.*?\))/g);
1166
1167 for (var j = 0, l = hls.length; j < l; j++) {
1168 var
1169 cfg = trim(hls[j]).split(/\s+/),
1170 hlCfg = {}
1171 ;
1172
1173 if (cfg[0] && cfg[0] != '') {
1174 hlCfg.from = cfg[0];
1175 }
1176
1177 if (cfg[1] && cfg[1] != '') {
1178 hlCfg.to = cfg[1];
1179 }
1180
1181 if (cfg[2] && cfg[2] != '') {
1182 hlCfg.color = cfg[2];
1183 }
1184
1185 config.highlights.push(hlCfg);
1186 }
1187 break;
1188 }
1189 case 'animation' :
1190 {
1191 if (cfgProp[1]) {
1192 if (!config.animation) {
1193 config.animation = {};
1194 }
1195
1196 if (cfgProp[1] == 'fn' && /^\s*function\s*\(/.test(attrValue)) {
1197 attrValue = eval('(' + attrValue + ')');
1198 }
1199
1200 config.animation[cfgProp[1]] = attrValue;
1201 }
1202 break;
1203 }
1204 default :
1205 {
1206 var cfgName = toCamelCase(cfgProp);
1207
1208 if (cfgName == 'onready') {
1209 continue;
1210 }
1211
1212 if (cfgName == 'majorTicks') {
1213 attrValue = attrValue.split(/\s+/);
1214 }
1215 else if (cfgName == 'strokeTicks' || cfgName == 'glow') {
1216 attrValue = attrValue == 'true' ? true : false;
1217 }
1218 else if (cfgName == 'valueFormat') {
1219 var val = attrValue.split('.');
1220
1221 if (val.length == 2) {
1222 attrValue = {
1223 'int': parseInt(val[0], 10),
1224 'dec': parseInt(val[1], 10)
1225 }
1226 }
1227 else {
1228 continue;
1229 }
1230 }
1231
1232 config[cfgName] = attrValue;
1233 break;
1234 }
1235 }
1236 }
1237 }
1238
1239 var g = new Gauge(config);
1240
1241 if (gauge.getAttribute('data-value')) {
1242 g.setRawValue(parseFloat(gauge.getAttribute('data-value')));
1243 }
1244
1245 if (gauge.getAttribute('data-onready')) {
1246 g.onready = function () {
1247 eval(this.config.renderTo.getAttribute('data-onready'));
1248 };
1249 }
1250
1251 g.draw();
1252 }
1253 }
1254 });
1255 module.exports = Gauge;
1256 // window['Gauge'] = Gauge;
1257
1258 // })(window);