Rift.IO OSM R1 Initial Submission
[osm/UI.git] / skyquake / framework / js / gauge-modified.js
diff --git a/skyquake/framework/js/gauge-modified.js b/skyquake/framework/js/gauge-modified.js
new file mode 100644 (file)
index 0000000..ad881c3
--- /dev/null
@@ -0,0 +1,1258 @@
+
+/*
+ * 
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+// (function(window){
+  /**!
+   * @license
+   * HTML5 Canvas Gauge implementation
+   *
+   * This code is subject to MIT license.
+   *
+   * Copyright (c) 2012 Mykhailo Stadnyk <mikhus@gmail.com>
+   *
+   * Permission is hereby granted, free of charge, to any person obtaining a copy of
+   * this software and associated documentation files (the "Software"), to deal in
+   * the Software without restriction, including without limitation the rights to use,
+   * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+   * Software, and to permit persons to whom the Software is furnished to do so,
+   * subject to the following conditions:
+   *
+   * The above copyright notice and this permission notice shall be included in all
+   * copies or substantial portions of the Software.
+   *
+   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+   *
+   * @authors: Mykhailo Stadnyk <mikhus@gmail.com>
+   *           Chris Poile <poile@edwards.usask.ca>
+   *           Luca Invernizzi <http://www.lucainvernizzi.net>
+   *           Robert Blackburn <http://www.rwblackburn.com>
+   */
+
+  /**
+   * @param {Object} config
+   * @constructor
+   */
+  var Gauge = function (config) {
+    Gauge.Collection.push(this);
+
+    /**
+     *  Default gauge configuration
+     *  @struct
+     */
+    this.config = {
+      isAggregate: false,
+      renderTo: null,
+      width: 200,
+      height: 200,
+      title: false,
+      maxValue: 100,
+      minValue: 0,
+      majorTicks: [],
+      minorTicks: 10,
+      strokeTicks: true,
+      units: false,
+      valueFormat: { "int": 3, "dec": 2 },
+      majorTicksFormat: { "int": 1, "dec": 0 },
+      glow: true,
+      animation: {
+        delay: 10,
+        duration: 250,
+        fn: 'cycle'
+      },
+      colors: {
+        plate: '#fff',
+        majorTicks: '#444',
+        minorTicks: '#666',
+        title: '#888',
+        units: '#888',
+        numbers: '#444',
+        needle: { start: 'rgba(240, 128, 128, 1)', end: 'rgba(255, 160, 122, .9)' }
+      },
+      highlights: [
+        {
+          from: 20,
+          to: 60,
+          color: '#eee'
+        },
+        {
+          from: 60,
+          to: 80,
+          color: '#ccc'
+        },
+        {
+          from: 80,
+          to: 100,
+          color: '#999'
+        }
+      ]
+    };
+
+    var
+      value = 0,
+      self = this,
+      fromValue = 0,
+      toValue = 0,
+      imready = false
+      ;
+
+    /**
+     * Sets a new value to gauge and updates the gauge view
+     *
+     * @param {number} val  - the new value to set to the gauge
+     * @return {Gauge} this - returns self
+     */
+    this.setValue = function (val) {
+
+      fromValue = config.animation ? value : val;
+
+      var dv = (config.maxValue - config.minValue) / 100;
+
+      toValue = val > config.maxValue ?
+      config.maxValue + dv :
+        val < config.minValue ?
+        config.minValue - dv :
+          val
+      ;
+
+      value = val;
+
+      if (toValue >= fromValue) {
+        config.arrow = 'up';
+      } else {
+        config.arrow = 'down';
+      }
+
+      config.animation ? animate() : this.draw();
+
+      return this;
+    };
+
+    /**
+     * Sets a new value to gauge and updates the gauge view without
+     * any animation (even if configured)
+     *
+     * @param {number} val  - the new value to set to the gauge
+     * @return {Gauge} this - returns self
+     */
+    this.setRawValue = function (val) {
+      fromValue = value = val;
+      this.draw();
+      return this;
+    };
+
+    /**
+     * Clears the value of the gauge
+     * @return {Gauge}
+     */
+    this.clear = function () {
+      value = fromValue = toValue = this.config.minValue;
+      this.draw();
+      return this;
+    };
+
+
+    /**
+     * Returns the current value been set to the gauge
+     *
+     * @return {number} value - current gauge's value
+     */
+    this.getValue = function () {
+      return value;
+    };
+
+    /**
+     * Ready event for the gauge. Use it whenever you
+     * initialize the gauge to be assured it was fully drawn
+     * before you start the update on it
+     *
+     * @event {Function} onready
+     */
+    this.onready = function () {
+    };
+
+    function applyRecursive(dst, src) {
+      for (var i in src) {
+        // modification by Chris Poile, Oct 08, 2012. More correct check of an Array instance
+        if (typeof src[i] == "object" && !(Object.prototype.toString.call(src[i]) === '[object Array]') && i != 'renderTo') {
+          if (typeof dst[i] != "object") {
+            dst[i] = {};
+          }
+
+          applyRecursive(dst[i], src[i]);
+        } else {
+          dst[i] = src[i];
+        }
+      }
+    };
+
+    applyRecursive(this.config, config);
+
+    this.config.minValue = parseFloat(this.config.minValue);
+    this.config.maxValue = parseFloat(this.config.maxValue);
+
+    config = this.config;
+    fromValue = value = config.minValue;
+
+    if (!config.renderTo) {
+      throw Error("Canvas element was not specified when creating the Gauge object!");
+    }
+
+    var
+      canvas = config.renderTo.tagName ? config.renderTo : document.getElementById(config.renderTo),
+      ctx = canvas.getContext('2d'),
+      cache, CW, CH, CX, CY, max, cctx
+      ;
+
+    function baseInit() {
+      canvas.width = config.width;
+      canvas.height = config.height;
+
+      cache = canvas.cloneNode(true);
+      cctx = cache.getContext('2d');
+      CW = canvas.width;
+      CH = canvas.height;
+      CX = CW / 2;
+      CY = CH / 2;
+      max = CX < CY ? CX : CY;
+
+      cache.i8d = false;
+
+      // translate cache to have 0, 0 in center
+      cctx.translate(CX, CY);
+      cctx.save();
+
+      // translate canvas to have 0,0 in center
+      ctx.translate(CX, CY);
+      ctx.save();
+    };
+
+    // do basic initialization
+    baseInit();
+
+    /**
+     * Updates the gauge config
+     *
+     * @param  {Object} config
+     * @return {Gauge}
+     */
+    this.updateConfig = function (config) {
+      applyRecursive(this.config, config);
+      baseInit();
+      this.draw();
+      return this;
+    };
+
+    var animateFx = {
+      linear: function (p) {
+        return p;
+      },
+      quad: function (p) {
+        return Math.pow(p, 2);
+      },
+      quint: function (p) {
+        return Math.pow(p, 5);
+      },
+      cycle: function (p) {
+        return 1 - Math.sin(Math.acos(p));
+      },
+      bounce: function (p) {
+        return 1 - (function (p) {
+            for (var a = 0, b = 1; 1; a += b, b /= 2) {
+              if (p >= (7 - 4 * a) / 11) {
+                return -Math.pow((11 - 6 * a - 11 * p) / 4, 2) + Math.pow(b, 2);
+              }
+            }
+          })(1 - p);
+      },
+      elastic: function (p) {
+        return 1 - (function (p) {
+            var x = 1.5;
+            return Math.pow(2, 10 * (p - 1)) * Math.cos(20 * Math.PI * x / 3 * p);
+          })(1 - p);
+      }
+    };
+
+    var animateInterval = null;
+
+    function _animate(opts) {
+      var start = new Date;
+
+      animateInterval = setInterval(function () {
+        var
+          timePassed = new Date - start,
+          progress = timePassed / opts.duration
+          ;
+
+        if (progress > 1) {
+          progress = 1;
+        }
+
+        var animateFn = typeof opts.delta == "function" ?
+            opts.delta :
+            animateFx[opts.delta]
+          ;
+
+        var delta = animateFn(progress);
+        opts.step(delta);
+
+        if (progress == 1) {
+          clearInterval(animateInterval);
+        }
+      }, opts.delay || 10);
+    };
+
+    function animate() {
+      animateInterval && clearInterval(animateInterval); // stop previous animation
+      var
+        path = (toValue - fromValue),
+        from = fromValue,
+        cfg = config.animation
+        ;
+
+      _animate({
+        delay: cfg.delay,
+        duration: cfg.duration,
+        delta: cfg.fn,
+        step: function (delta) {
+          fromValue = parseFloat(from) + path * delta;
+          self.draw();
+        }
+      });
+    };
+
+    // defaults
+    ctx.lineCap = "round";
+
+    /**
+     * Drows the gauge. Normally this function should be used to
+     * initally draw the gauge
+     *
+     * @return {Gauge} this - returns the self Gauge object
+     */
+    this.draw = function () {
+      if (!cache.i8d) {
+        // clear the cache
+        cctx.clearRect(-CX, -CY, CW, CH);
+        cctx.save();
+
+        var tmp = {ctx: ctx};
+        ctx = cctx;
+
+        drawPlate();
+        drawHighlights();
+        drawMinorTicks();
+        drawMajorTicks();
+        drawNumbers();
+        drawTitle();
+        drawUnits();
+
+        cache.i8d = true;
+        ctx = tmp.ctx;
+        delete tmp.ctx;
+      }
+
+      // clear the canvas
+      ctx.clearRect(-CX, -CY, CW, CH);
+      ctx.save();
+
+      ctx.drawImage(cache, -CX, -CY, CW, CH);
+
+      if (!Gauge.initialized) {
+        var iv = setInterval(function () {
+          if (!Gauge.initialized) {
+            return;
+          }
+
+          clearInterval(iv);
+
+          drawValueBox();
+          drawNeedle();
+          // drawArrow();
+
+          if (!imready) {
+            self.onready && self.onready();
+            imready = true;
+          }
+        }, 10);
+      } else {
+        drawValueBox();
+        drawNeedle();
+        // drawArrow();
+
+        if (!imready) {
+          self.onready && self.onready();
+          imready = true;
+        }
+      }
+
+      return this;
+    };
+
+    /**
+     * Transforms degrees to radians
+     */
+    function radians(degrees) {
+      return degrees * Math.PI / 180;
+    };
+
+    /**
+     * Linear gradient
+     */
+    function lgrad(clrFrom, clrTo, len) {
+      var grad = ctx.createLinearGradient(0, 0, 0, len);
+      grad.addColorStop(0, clrFrom);
+      grad.addColorStop(1, clrTo);
+
+      return grad;
+    };
+
+    function drawPlate() {
+      var
+        r0 = max / 100 * 93,
+        d0 = max - r0,
+        r1 = max / 100 * 91,
+        d1 = max - r1,
+        r2 = max / 100 * 88,
+        d2 = max - r2,
+        r3 = max / 100 * 85;
+
+      ctx.save();
+
+      if (config.glow) {
+        ctx.shadowBlur = d0;
+        ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
+      }
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r0, 0, Math.PI * 2, true);
+      // ctx.fillStyle = lgrad( '#ddd', '#aaa', r0);
+      ctx.fillStyle = lgrad('hsla(0, 0%, 17%, 1)', 'hsla(0, 0%, 0%, 1)', r0);
+      //ctx.fill();
+
+      //ctx.restore();
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r1, 0, Math.PI * 2, true);
+      // ctx.fillStyle = lgrad( '#fafafa', '#ccc', r1);
+      ctx.fillStyle = lgrad('hsla(0, 0%, 47%, 1)', 'hsla(0, 0%, 33%, 1)', r1);
+      //ctx.fill();
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r2, 0, Math.PI * 2, true);
+      // ctx.fillStyle = lgrad( '#eee', '#f0f0f0', r2);
+      ctx.fillStyle = lgrad('hsla(0, 0%, 33%, 1)', 'hsla(0, 0%, 43%, 1)', r2);
+      //ctx.fill();
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r3, 0, Math.PI * 2, true);
+      ctx.fillStyle = config.colors.plate;
+      //ctx.fill();
+
+      ctx.save();
+    };
+
+    /**
+     * Formats a number for display on the dial's plate using the majorTicksFormat config option.
+     *
+     * @param {number} num The number to format
+     * @returns {string} The formatted number
+     */
+    function formatMajorTickNumber(num) {
+      var r, isDec = false;
+
+      // First, force the correct number of digits right of the decimal.
+      if (config.majorTicksFormat.dec === 0) {
+        r = Math.round(num).toString();
+      } else {
+        r = num.toFixed(config.majorTicksFormat.dec);
+      }
+
+      // Second, force the correct number of digits left of the decimal.
+      if (config.majorTicksFormat["int"] > 1) {
+        // Does this number have a decimal?
+        isDec = (r.indexOf('.') > -1);
+
+        // Is this number a negative number?
+        if (r.indexOf('-') > -1) {
+          return '-' + [
+              config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 2 + (isDec ? 1 : 0) - r.length
+            ].join('0') + r.replace('-', '');
+        } else {
+          return [
+              config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 1 + (isDec ? 1 : 0) - r.length
+            ].join('0') + r;
+        }
+      } else {
+        return r;
+      }
+    }
+
+    // major ticks draw
+    function drawMajorTicks() {
+      var r = max / 100 * 81;
+
+      ctx.lineWidth = 1;
+      ctx.strokeStyle = config.colors.majorTicks;
+      ctx.save();
+
+      if (config.majorTicks.length === 0) {
+        var numberOfDefaultTicks = 5;
+        var tickSize = (config.maxValue - config.minValue) / numberOfDefaultTicks;
+
+        for (var i = 0; i < numberOfDefaultTicks; i++) {
+          config.majorTicks.push(formatMajorTickNumber(config.minValue + (tickSize * i)));
+        }
+        config.majorTicks.push(formatMajorTickNumber(config.maxValue));
+      }
+
+      for (var i = 0; i < config.majorTicks.length; ++i) {
+        var a = 45 + i * (270 / (config.majorTicks.length - 1));
+        ctx.rotate(radians(a));
+
+        ctx.beginPath();
+        ctx.moveTo(0, r);
+        ctx.lineTo(0, r - max / 100 * 15);
+        ctx.stroke();
+
+        ctx.restore();
+        ctx.save();
+      }
+
+      if (config.strokeTicks) {
+        ctx.rotate(radians(90));
+
+        ctx.beginPath();
+        ctx.arc(0, 0, r, radians(45), radians(315), false);
+        ctx.stroke();
+        ctx.restore();
+
+        ctx.save();
+      }
+    };
+
+    // minor ticks draw
+    function drawMinorTicks() {
+      var r = max / 100 * 81;
+
+      ctx.lineWidth = 1;
+      ctx.strokeStyle = config.colors.minorTicks;
+
+      ctx.save();
+
+      var len = config.minorTicks * (config.majorTicks.length - 1);
+
+      for (var i = 0; i < len; ++i) {
+        var a = 45 + i * (270 / len);
+        ctx.rotate(radians(a));
+
+        ctx.beginPath();
+        ctx.moveTo(0, r);
+        ctx.lineTo(0, r - max / 100 * 7.5);
+        ctx.stroke();
+
+        ctx.restore();
+        ctx.save();
+      }
+    };
+
+    // tick numbers draw
+    function drawNumbers() {
+      //var r = max / 100 * 55;
+      //
+      //for (var i = 0; i < config.majorTicks.length; ++i) {
+      //  var
+      //    a = 45 + i * (270 / (config.majorTicks.length - 1)),
+      //    p = rpoint(r, radians(a))
+      //    ;
+      //
+      //  ctx.font = 20 * (max / 200) + "px Arial";
+      //  ctx.fillStyle = config.colors.numbers;
+      //  ctx.lineWidth = 0;
+      //  ctx.textAlign = "center";
+      //  ctx.fillText(config.majorTicks[i], p.x, p.y + 3);
+      //}
+    };
+
+    // title draw
+    function drawTitle() {
+      if (!config.title) {
+        return;
+      }
+
+      ctx.save();
+      ctx.font = 24 * (max / 200) + "px Arial";
+      ctx.fillStyle = config.colors.title;
+      ctx.textAlign = "center";
+      ctx.fillText(config.title, 0, -max / 4.25);
+      ctx.restore();
+    };
+
+    // units draw
+    function drawUnits() {
+      if (!config.units) {
+        return;
+      }
+
+      ctx.save();
+      ctx.font = 22 * (max / 200) + "px Arial";
+      ctx.fillStyle = config.colors.units;
+      ctx.textAlign = "center";
+      ctx.fillText(config.units, 0, max / 3.25);
+      ctx.restore();
+    };
+
+
+    function drawArrow() {
+
+      if (config.arrow != "false") {
+
+        if (config.arrow == "up") {
+          var r = max / 100 * 2.0;
+          y0 = max / 100 * 64;
+          y1 = max / 100 * 55;
+          y2 = max / 100 * 53;
+          y3 = max / 100 * 50;
+          y4 = max / 100 * 45;
+          arrow_color = "rgba(107, 184, 20, 1)";
+
+          var x0 = max / 100 * -8
+          var x1 = max / 100 * -6
+          var x2 = max / 100 * -1.5
+          var x3 = max / 100 * 0
+          var x4 = max / 100 * 1.5
+          var x5 = max / 100 * 6
+          var x6 = max / 100 * 8
+
+          ctx.beginPath();
+          ctx.moveTo(x0, y2);
+          ctx.lineTo(x3 - r, y4 + r);
+          ctx.arcTo(x3, y4 - r, x3 + r, y4 + r, r * 1.09);
+          ctx.lineTo(x6, y2);
+          ctx.arcTo(x6 + r/2.0, y2 + r/1.0, x5, y1, r*.9)
+          ctx.lineTo(x4, y3);
+          ctx.lineTo(x4, y0);
+          ctx.arcTo(x3, y0 + r, x2, y0, r*.9);
+          ctx.lineTo(x2, y3);
+          ctx.lineTo(x1, y1);
+          ctx.arcTo(x1 - r, y1 - r/2.0, x0, y2, r*1.09)
+
+
+          ctx.closePath();
+          ctx.fillStyle = arrow_color;
+          ctx.fill();
+        } else {
+          var r = max / 100 * 2.0;
+          var y0 = max / 100 * 45;
+          var y1 = max / 100 * 54;
+          var y2 = max / 100 * 56;
+          var y3 = max / 100 * 59;
+          var y4 = max / 100 * 64;
+          var arrow_color = "rgba(252, 38, 50, 1)";
+
+          var x0 = max / 100 * -8
+          var x1 = max / 100 * -6
+          var x2 = max / 100 * -1.5
+          var x3 = max / 100 * 0
+          var x4 = max / 100 * 1.5
+          var x5 = max / 100 * 6
+          var x6 = max / 100 * 8
+
+          ctx.beginPath();
+          ctx.moveTo(x0, y2);
+          ctx.lineTo(x3 - r, y4 - r);
+          ctx.arcTo(x3, y4 + r, x3 + r, y4 - r, r * 1.09);
+          ctx.lineTo(x6, y2);
+          ctx.arcTo(x6 + r/2.0, y2 - r/1.0, x5, y1, r*.9)
+          ctx.lineTo(x4, y3);
+          ctx.lineTo(x4, y0);
+          ctx.arcTo(x3, y0 - r, x2, y0, r*.9);
+          ctx.lineTo(x2, y3);
+          ctx.lineTo(x1, y1);
+          ctx.arcTo(x1 - r, y1 + r/2.0, x0, y2, r*1.09)
+
+
+          ctx.closePath();
+          ctx.fillStyle = arrow_color;
+          ctx.fill();
+        }
+        ctx.save();
+        ctx.restore();
+      }
+    }
+
+    function padValue(val) {
+        var cdec = config.valueFormat['dec']
+        var cint = config.valueFormat['int']
+
+      val = parseFloat(val);
+      var n = (val < 0);
+
+      val = Math.abs(val);
+
+      if (cdec > 0) {
+        val = val.toFixed(cdec).toString().split('.');
+
+        for (var i = 0, s = cint - val[0].length; i < s; ++i) {
+          val[0] = '0' + val[0];
+        }
+
+        val = (n ? '-' : '') + val[0] + '.' + val[1];
+      } else {
+        val = Math.round(val).toString();
+
+        for (var i = 0, s = cint - val.length; i < s; ++i) {
+          val = '0' + val;
+        }
+
+        val = (n ? '-' : '') + val
+      }
+
+      return val;
+    };
+
+    function rpoint(r, a) {
+      var
+        x = 0, y = r,
+
+        sin = Math.sin(a),
+        cos = Math.cos(a),
+
+        X = x * cos - y * sin,
+        Y = x * sin + y * cos
+        ;
+
+      return { x: X, y: Y };
+    };
+    function clearCircle(x, y, radius)
+    {
+      ctx.beginPath();
+      ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
+      ctx.clip();
+      ctx.clearRect(x - radius - 1, y - radius - 1,
+        radius * 2 + 2, radius * 2 + 2);
+
+    };
+    // draws the highlight colors
+    function drawHighlights() {
+      ctx.save();
+
+      var r1 = max / 100 * 81;
+      var r2 = r1 - max / 100 * 15;
+
+      for (var i = 0, s = config.highlights.length; i < s; i++) {
+        var
+          hlt = config.highlights[i],
+          vd = (config.maxValue - config.minValue) / 270,
+          sa = radians(45 + (hlt.from - config.minValue) / vd),
+          ea = radians(45 + (hlt.to - config.minValue) / vd)
+          ;
+
+        ctx.beginPath();
+
+        ctx.rotate(radians(90));
+        ctx.arc(0, 0, r1, sa, ea, false);
+        ctx.restore();
+        ctx.save();
+
+        var
+          ps = rpoint(r2, sa),
+          pe = rpoint(r1, sa)
+          ;
+        ctx.moveTo(ps.x, ps.y);
+        ctx.lineTo(pe.x, pe.y);
+
+        var
+          ps1 = rpoint(r1, ea),
+          pe1 = rpoint(r2, ea)
+          ;
+
+        ctx.lineTo(ps1.x, ps1.y);
+        ctx.lineTo(pe1.x, pe1.y);
+        ctx.lineTo(ps.x, ps.y);
+
+        ctx.closePath();
+
+        ctx.fillStyle = hlt.color;
+        ctx.fill();
+
+        ctx.beginPath();
+        ctx.rotate(radians(90));
+        ctx.arc(0, 0, r2, sa - 0.2, ea + 0.2, false);
+        ctx.restore();
+
+        ctx.closePath();
+
+        ctx.fillStyle = config.colors.plate;
+        ctx.fill();
+        ctx.save();
+        ctx.imageSmoothingEnabled = true
+        //clearCircle(0, 0, 100)
+
+      }
+    };
+
+    // drows the gauge needle
+    function drawNeedle() {
+      var
+        r1 = 0 ,
+        r2 = 0 ,
+        rIn = max / 100 * 85,
+        rOut = max / 100 * 63,
+        rP = max / 100 * 59,
+        pad1 = max / 100 * 3,
+        pad2 = max / 100 * 2.5,
+
+        shad = function () {
+          ctx.shadowOffsetX = 2;
+          ctx.shadowOffsetY = 2;
+          ctx.shadowBlur = 10;
+          // ctx.shadowColor   = 'rgba(188, 143, 143, 0.45)';
+          ctx.shadowColor = 'rgba(50, 50, 50, .3)';
+        }
+        ;
+
+      shad();
+
+      ctx.save();
+
+      if (fromValue < 0) {
+        fromValue = Math.abs(config.minValue - fromValue);
+      } else if (config.minValue > 0) {
+        fromValue -= config.minValue
+      } else {
+        fromValue = Math.abs(config.minValue) + fromValue;
+      }
+
+      ctx.rotate(radians(45 + fromValue / ((config.maxValue - config.minValue) / 270)));
+
+      ctx.beginPath();
+      ctx.lineTo(-pad2, rOut);
+      ctx.lineTo(-pad2, rIn);
+      ctx.lineTo(pad2, rIn);
+      ctx.lineTo(pad2, rOut);
+      ctx.lineTo(0, rP);
+      ctx.closePath();
+      ctx.strokeStyle = "#999"
+      ctx.stroke();
+
+      ctx.fillStyle = lgrad(
+        config.colors.needle.start,
+        config.colors.needle.end,
+        rIn - rOut
+      );
+      ctx.fill();
+
+      // ctx.beginPath();
+      // ctx.lineTo(-_pad2, _rOut);
+      // ctx.lineTo(-_pad2, _rIn);
+      // ctx.lineTo(_pad2, _rIn);
+      // ctx.lineTo(_pad2, _rOut);
+      // ctx.lineTo(0, _rOut - 5);
+      // ctx.closePath();
+
+      // ctx.fillStyle = "#ccc"
+      // ctx.fill();
+
+      ctx.beginPath();
+      ctx.lineTo(-pad2, rIn);
+      ctx.lineTo(-pad2, rIn);
+      ctx.lineTo(-pad1, 0);
+      ctx.lineTo(-pad2, rOut);
+      ctx.lineTo(pad2 / 2 - 2, rOut);
+      ctx.closePath();
+      ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
+
+      //ctx.fill();
+
+      ctx.restore();
+
+      //shad();
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r2 +.5, 0, Math.PI * 2, true);
+      // ctx.fillStyle = lgrad( '#f0f0f0', '#ccc', r1);
+      ctx.fillStyle = lgrad('#3b3b3b', '#121212', r1);
+      //ctx.fill();
+
+      ctx.restore();
+
+      ctx.beginPath();
+      ctx.arc(0, 0, r2, 0, Math.PI * 2, true);
+      // ctx.fillStyle = lgrad( "#e8e8e8", "#f5f5f5", r2);
+      ctx.fillStyle = 'rgba(255,255,255,1)';
+      //ctx.fill();
+    };
+
+    function roundRect(x, y, w, h, r) {
+      ctx.beginPath();
+
+      ctx.moveTo(x + r, y);
+      ctx.lineTo(x + w - r, y);
+
+      ctx.quadraticCurveTo(x + w, y, x + w, y + r);
+      ctx.lineTo(x + w, y + h - r);
+
+      ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
+      ctx.lineTo(x + r, y + h);
+
+      ctx.quadraticCurveTo(x, y + h, x, y + h - r);
+      ctx.lineTo(x, y + r);
+
+      ctx.quadraticCurveTo(x, y, x + r, y);
+
+      ctx.closePath();
+    };
+
+    // value box draw
+    function drawValueBox() {
+      ctx.save();
+
+      ctx.font = 100 +  " " + 73 * (max / 200) + "px 'roboto-light";
+      var
+        text = padValue(value),
+        tw = ctx.measureText('-' + padValue(0)).width,
+        y = max - max / 100 * 96,
+        x = 0,
+        th = 0.12 * max
+        ;
+
+      ctx.save();
+
+      roundRect(
+        -tw / 2 - 0.025 * max,
+        y - th - 0.04 * max,
+        tw + 0.05 * max,
+        th + 0.07 * max,
+        0.025 * max
+      );
+
+      var grd = ctx.createRadialGradient(
+        x,
+        y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2,
+        max / 10,
+        x,
+        y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2,
+        max / 5
+      );
+
+      // grd.addColorStop( 0, "#888");
+      //    grd.addColorStop( 1, "#666");
+
+      // ctx.strokeStyle = grd;
+      // ctx.lineWidth = 0.05 * max;
+      // ctx.stroke();
+
+      // ctx.shadowBlur  = 0.012 * max;
+      // ctx.shadowColor = 'rgba(0, 0, 0, 1)';
+
+      // ctx.fillStyle = "#babab2";
+      // ctx.fill();
+
+      // ctx.restore();
+
+      // ctx.shadowOffsetX = 0.004 * max;
+      // ctx.shadowOffsetY = 0.004 * max;
+      // ctx.shadowBlur    = 0.012 * max;
+      // ctx.shadowColor   = 'rgba(0, 0, 0, 0.3)';
+
+      // ctx.fillStyle = "#444";
+      ctx.fillStyle = "rgba(50,50,50,1)";
+      //      ctx.fillStyle = "rgba(50,50,50,1)";
+      ctx.textAlign = "center";
+
+      ctx.fillText(text, -x, y);
+
+
+      ctx.save();
+      ctx.font = 100 +  " " + 20 * (max / 200) + "px 'roboto-light";
+      //ctx.fillText(config.unit, -x, y + 30);
+      ctx.restore();
+
+    };
+  };
+
+// initialize
+  Gauge.initialized = false;
+  (function () {
+    var
+      d = document,
+      h = d.getElementsByTagName('head')[0],
+      ie = navigator.userAgent.toLocaleLowerCase().indexOf('msie') != -1,
+      url = 'fonts/digital-7-mono.' + (ie ? 'eot' : 'ttf'),
+
+    // RW: don't use mono font, this was causing err in js console
+      text = '',
+//        text = "@font-face {" +
+//            "font-family: 'Led';" +
+//            "src: url('" + url + "');" +
+//            "}",
+      ss,
+      r = d.createElement('style')
+      ;
+
+    r.type = 'text/css';
+
+    if (ie) {
+      h.appendChild(r);
+      ss = r.styleSheet;
+      ss.cssText = text;
+    } else {
+      try {
+        r.appendChild(d.createTextNode(text));
+      } catch (e) {
+        r.cssText = text;
+      }
+
+      h.appendChild(r);
+
+      ss = r.styleSheet ? r.styleSheet :
+        (r.sheet || d.styleSheets[d.styleSheets.length - 1])
+      ;
+    }
+
+    var iv = setInterval(function () {
+      if (!d.body) {
+        return;
+      }
+
+      clearInterval(iv);
+
+      var dd = d.createElement('div');
+
+      dd.style.fontFamily = 'Led';
+      dd.style.position = 'absolute';
+      dd.style.height = dd.style.width = 0;
+      dd.style.overflow = 'hidden';
+
+      dd.innerHTML = '.';
+
+      d.body.appendChild(dd);
+
+      setTimeout(function () { // no other way to handle font is rendered by a browser
+        // just give the browser around 250ms to do that :(
+        Gauge.initialized = true;
+        dd.parentNode.removeChild(dd);
+      }, 250);
+    }, 1);
+  })();
+
+  Gauge.Collection = [];
+  Gauge.Collection.get = function (id) {
+    var self = this;
+
+    if (typeof(id) == 'string') {
+      for (var i = 0, s = self.length; i < s; i++) {
+        var canvas = self[i].config.renderTo.tagName ? self[i].config.renderTo : document.getElementById(self[i].config.renderTo);
+        if (canvas.getAttribute('id') == id) {
+          return self[i];
+        }
+      }
+    } else if (typeof(id) == 'number') {
+      return self[id];
+    } else {
+      return null;
+    }
+  };
+
+  function domReady(handler) {
+    if (window.addEventListener) {
+      window.addEventListener('DOMContentLoaded', handler, false);
+    } else {
+      window.attachEvent('onload', handler);
+    }
+  }
+
+  domReady(function () {
+    function toCamelCase(arr) {
+      var str = arr[0];
+      for (var i = 1, s = arr.length; i < s; i++) {
+        str += arr[i].substr(0, 1).toUpperCase() + arr[i].substr(1, arr[i].length - 1);
+      }
+      return str;
+    };
+
+    function trim(str) {
+      return str.replace(/^\s+|\s+$/g, '');
+    };
+
+    var c = document.getElementsByTagName('canvas');
+
+    for (var i = 0, s = c.length; i < s; i++) {
+
+      if (c[i].getAttribute('data-type') == 'canv-gauge') {
+        var
+          gauge = c[i],
+          config = {},
+          prop,
+          w = parseInt(gauge.getAttribute('width'), 10),
+          h = parseInt(gauge.getAttribute('height'), 10)
+          ;
+
+        config.renderTo = gauge;
+
+        if (w) {
+          config.width = w;
+        }
+
+        if (h) {
+          config.height = h;
+        }
+
+        for (var ii = 0, ss = gauge.attributes.length; ii < ss; ii++) {
+          prop = gauge.attributes.item(ii).nodeName;
+
+          if (prop != 'data-type' && prop.substr(0, 5) == 'data-') {
+            var
+              cfgProp = prop.substr(5, prop.length - 5).toLowerCase().split('-'),
+              attrValue = gauge.getAttribute(prop)
+              ;
+
+            if (!attrValue) {
+              continue;
+            }
+
+            switch (cfgProp[0]) {
+              case 'colors' :
+              {
+                if (cfgProp[1]) {
+                  if (!config.colors) {
+                    config.colors = {};
+                  }
+
+                  if (cfgProp[1] == 'needle') {
+                    var parts = attrValue.split(/\s+/);
+
+                    if (parts[0] && parts[1]) {
+                      config.colors.needle = { start: parts[0], end: parts[1] };
+                    }
+                    else {
+                      config.colors.needle = attrValue;
+                    }
+                  }
+                  else {
+                    cfgProp.shift();
+                    config.colors[toCamelCase(cfgProp)] = attrValue;
+                  }
+                }
+                break;
+              }
+              case 'highlights' :
+              {
+                if (!config.highlights) {
+                  config.highlights = [];
+                }
+
+                var hls = attrValue.match(/(?:(?:-?\d*\.)?(-?\d+){1,2} ){2}(?:(?:#|0x)?(?:[0-9A-F|a-f]){3,8}|rgba?\(.*?\))/g);
+
+                for (var j = 0, l = hls.length; j < l; j++) {
+                  var
+                    cfg = trim(hls[j]).split(/\s+/),
+                    hlCfg = {}
+                    ;
+
+                  if (cfg[0] && cfg[0] != '') {
+                    hlCfg.from = cfg[0];
+                  }
+
+                  if (cfg[1] && cfg[1] != '') {
+                    hlCfg.to = cfg[1];
+                  }
+
+                  if (cfg[2] && cfg[2] != '') {
+                    hlCfg.color = cfg[2];
+                  }
+
+                  config.highlights.push(hlCfg);
+                }
+                break;
+              }
+              case 'animation' :
+              {
+                if (cfgProp[1]) {
+                  if (!config.animation) {
+                    config.animation = {};
+                  }
+
+                  if (cfgProp[1] == 'fn' && /^\s*function\s*\(/.test(attrValue)) {
+                    attrValue = eval('(' + attrValue + ')');
+                  }
+
+                  config.animation[cfgProp[1]] = attrValue;
+                }
+                break;
+              }
+              default :
+              {
+                var cfgName = toCamelCase(cfgProp);
+
+                if (cfgName == 'onready') {
+                  continue;
+                }
+
+                if (cfgName == 'majorTicks') {
+                  attrValue = attrValue.split(/\s+/);
+                }
+                else if (cfgName == 'strokeTicks' || cfgName == 'glow') {
+                  attrValue = attrValue == 'true' ? true : false;
+                }
+                else if (cfgName == 'valueFormat') {
+                  var val = attrValue.split('.');
+
+                  if (val.length == 2) {
+                    attrValue = {
+                      'int': parseInt(val[0], 10),
+                      'dec': parseInt(val[1], 10)
+                    }
+                  }
+                  else {
+                    continue;
+                  }
+                }
+
+                config[cfgName] = attrValue;
+                break;
+              }
+            }
+          }
+        }
+
+        var g = new Gauge(config);
+
+        if (gauge.getAttribute('data-value')) {
+          g.setRawValue(parseFloat(gauge.getAttribute('data-value')));
+        }
+
+        if (gauge.getAttribute('data-onready')) {
+          g.onready = function () {
+            eval(this.config.renderTo.getAttribute('data-onready'));
+          };
+        }
+
+        g.draw();
+      }
+    }
+  });
+module.exports = Gauge;
+  // window['Gauge'] = Gauge;
+
+// })(window);