freeboard/js/justgage.1.0.1.js

491 lines
16 KiB
JavaScript
Executable File

/**
* JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges.
* Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com
* Licensed under MIT.
* Date: 31/07/2012
* @author Bojan Djuricic (@Toorshia)
* @version 1.0
*
* http://www.justgage.com
*/
JustGage = function(config)
{
if(!config.id)
{
alert("Missing id parameter for gauge!");
return false;
}
if(!document.getElementById(config.id))
{
alert("No element with id: \"" + config.id + "\" found!");
return false;
}
// configurable parameters
this.config = {
// id : string
// this is container element id
id : config.id,
// value : int
// value gauge is showing
value : (config.value) ? config.value : 0,
// valueFontColor : string
// color of label showing current value
valueFontColor : (config.valueFontColor) ? config.valueFontColor : "#010101",
// min : int
// min value
min : (config.min) ? config.min : 0,
// max : int
// max value
max : (config.max) ? config.max : 100,
// showMinMax : bool
// hide or display min and max values
showMinMax : (config.showMinMax != null) ? config.showMinMax : true,
// gaugeWidthScale : float
// width of the gauge element
gaugeWidthScale : (config.gaugeWidthScale) ? config.gaugeWidthScale : 1.0,
// gaugeColor : string
// background color of gauge element
gaugeColor : (config.gaugeColor) ? config.gaugeColor : "#edebeb",
// label : string
// text to show below value
label : (config.label) ? config.label : "",
// showInnerShadow : bool
// give gauge element small amount of inner shadow
showInnerShadow : (config.showInnerShadow != null) ? config.showInnerShadow : true,
// shadowOpacity : int
// 0 ~ 1
shadowOpacity : (config.shadowOpacity) ? config.shadowOpacity : 0.2,
// shadowSize: int
// inner shadow size
shadowSize : (config.shadowSize) ? config.shadowSize : 5,
// shadowVerticalOffset : int
// how much shadow is offset from top
shadowVerticalOffset: (config.shadowVerticalOffset) ? config.shadowVerticalOffset : 3,
// levelColors : string[]
// colors of indicator, from lower to upper, in RGB format
levelColors : (config.levelColors) ? config.levelColors : percentColors,
// levelColorsGradient : bool
// whether to use gradual color change for value, or sector-based
levelColorsGradient : (config.levelColorsGradient != null) ? config.levelColorsGradient : true,
// labelFontColor : string
// color of label showing label under value
labelFontColor : (config.labelFontColor) ? config.labelFontColor : "#b3b3b3",
// startAnimationTime : int
// length of initial animation
startAnimationTime : (config.startAnimationTime) ? config.startAnimationTime : 700,
// startAnimationType : string
// type of initial animation (linear, >, <, <>, bounce)
startAnimationType : (config.startAnimationType) ? config.startAnimationType : ">",
// refreshAnimationTime : int
// length of refresh animation
refreshAnimationTime: (config.refreshAnimationTime) ? config.refreshAnimationTime : 700,
// refreshAnimationType : string
// type of refresh animation (linear, >, <, <>, bounce)
refreshAnimationType: (config.refreshAnimationType) ? config.refreshAnimationType : ">"
};
// overflow values
if(config.value > this.config.max) this.config.value = this.config.max;
if(config.value < this.config.min) this.config.value = this.config.min;
this.originalValue = config.value;
// canvas
this.canvas = Raphael(this.config.id, "100%", "100%");
// canvas dimensions
//var canvasW = document.getElementById(this.config.id).clientWidth;
//var canvasH = document.getElementById(this.config.id).clientHeight;
var canvasW = getStyle(document.getElementById(this.config.id), "width").slice(0, -2) * 1;
var canvasH = getStyle(document.getElementById(this.config.id), "height").slice(0, -2) * 1;
// widget dimensions
var widgetW, widgetH;
if((canvasW / canvasH) > 1.25)
{
widgetW = 1.25 * canvasH;
widgetH = canvasH;
}
else
{
widgetW = canvasW;
widgetH = canvasW / 1.25;
}
// delta
var dx = (canvasW - widgetW) / 2;
var dy = (canvasH - widgetH) / 2;
// value
var valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 6.4) : 16;
var valueX = dx + widgetW / 2;
var valueY = dy + widgetH / 1.4;
// label
var labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var labelX = dx + widgetW / 2;
//var labelY = dy + widgetH / 1.126760563380282;
var labelY = valueY + valueFontSize / 2 + 6;
// min
var minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2;
var minY = dy + widgetH / 1.126760563380282;
// max
var maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2;
var maxY = dy + widgetH / 1.126760563380282;
// parameters
this.params = {
canvasW : canvasW,
canvasH : canvasH,
widgetW : widgetW,
widgetH : widgetH,
dx : dx,
dy : dy,
valueFontSize: valueFontSize,
valueX : valueX,
valueY : valueY,
labelFontSize: labelFontSize,
labelX : labelX,
labelY : labelY,
minFontSize : minFontSize,
minX : minX,
minY : minY,
maxFontSize : maxFontSize,
maxX : maxX,
maxY : maxY
};
// pki - custom attribute for generating gauge paths
this.canvas.customAttributes.pki = function(value, min, max, w, h, dx, dy, gws)
{
var alpha = (1 - (value - min) / (max - min)) * Math.PI , Ro = w / 2 - w / 10, Ri = Ro - w / 6.666666666666667 * gws,
Cx = w / 2 + dx, Cy = h / 1.25 + dy,
Xo = w / 2 + dx + Ro * Math.cos(alpha), Yo = h - (h - Cy) + dy - Ro * Math.sin(alpha), Xi = w / 2 + dx + Ri * Math.cos(alpha), Yi = h - (h - Cy) + dy - Ri * Math.sin(alpha), path;
path += "M" + (Cx - Ri) + "," + Cy + " ";
path += "L" + (Cx - Ro) + "," + Cy + " ";
path += "A" + Ro + "," + Ro + " 0 0,1 " + Xo + "," + Yo + " ";
path += "L" + Xi + "," + Yi + " ";
path += "A" + Ri + "," + Ri + " 0 0,0 " + (Cx - Ri) + "," + Cy + " ";
path += "z ";
return { path: path };
}
// gauge
this.gauge = this.canvas.path().attr({
"stroke": "none",
"fill" : this.config.gaugeColor,
pki : [this.config.max, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH,
this.params.dx, this.params.dy, this.config.gaugeWidthScale]
});
this.gauge.id = this.config.id + "-gauge";
// level
this.level = this.canvas.path().attr({
"stroke": "none",
"fill" : getColorForPercentage((this.config.value - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient),
pki : [this.config.min, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH,
this.params.dx, this.params.dy, this.config.gaugeWidthScale]
});
this.level.id = this.config.id + "-level";
// value
this.txtValue = this.canvas.text(this.params.valueX, this.params.valueY, this.originalValue);
this.txtValue.attr({
"font-size" : this.params.valueFontSize,
"font-weight" : "bold",
"font-family" : "Arial",
"fill" : this.config.valueFontColor,
"fill-opacity": "0"
});
this.txtValue.id = this.config.id + "-txtvalue";
// label
this.txtLabel = this.canvas.text(this.params.labelX, this.params.labelY, this.config.label);
this.txtLabel.attr({
"font-size" : this.params.labelFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": "0"
});
this.txtLabel.id = this.config.id + "-txtlabel";
// min
this.txtMin = this.canvas.text(this.params.minX, this.params.minY, this.config.min);
this.txtMin.attr({
"font-size" : this.params.minFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": (this.config.showMinMax == true) ? "1" : "0"
});
this.txtMin.id = this.config.id + "-txtmin";
// max
this.txtMax = this.canvas.text(this.params.maxX, this.params.maxY, this.config.max);
this.txtMax.attr({
"font-size" : this.params.maxFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": (this.config.showMinMax == true) ? "1" : "0"
});
this.txtMax.id = this.config.id + "-txtmax";
var defs = this.canvas.canvas.childNodes[1];
var svg = "http://www.w3.org/2000/svg";
if(ie < 9)
{
onCreateElementNsReady(function()
{
this.generateShadow();
});
}
else
{
this.generateShadow(svg, defs);
}
// animate
this.level.animate({pki: [this.config.value, this.config.min, this.config.max, this.params.widgetW,
this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale
]}, this.config.startAnimationTime, this.config.startAnimationType);
this.txtValue.animate({"fill-opacity": "1"}, this.config.startAnimationTime, this.config.startAnimationType);
this.txtLabel.animate({"fill-opacity": "1"}, this.config.startAnimationTime, this.config.startAnimationType);
};
// refresh gauge level
JustGage.prototype.refresh = function(val)
{
// overflow values
originalVal = val;
if(val > this.config.max)
{
val = this.config.max;
}
if(val < this.config.min)
{
val = this.config.min;
}
var color = getColorForPercentage((val - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient);
this.canvas.getById(this.config.id + "-txtvalue").attr({"text": originalVal});
this.canvas.getById(this.config.id + "-level").animate({pki : [val,
this.config.min,
this.config.max,
this.params.widgetW,
this.params.widgetH,
this.params.dx,
this.params.dy,
this.config.gaugeWidthScale
], "fill" : color}, this.config.refreshAnimationTime, this.config.refreshAnimationType);
};
var percentColors = [
"#a9d70b", "#f9c802", "#ff0000"
]
JustGage.prototype.generateShadow = function(svg, defs)
{
// FILTER
var gaussFilter = document.createElementNS(svg, "filter");
gaussFilter.setAttribute("id", this.config.id + "-inner-shadow");
defs.appendChild(gaussFilter);
// offset
var feOffset = document.createElementNS(svg, "feOffset");
feOffset.setAttribute("dx", 0);
feOffset.setAttribute("dy", this.config.shadowVerticalOffset);
gaussFilter.appendChild(feOffset);
// blur
var feGaussianBlur = document.createElementNS(svg, "feGaussianBlur");
feGaussianBlur.setAttribute("result", "offset-blur");
feGaussianBlur.setAttribute("stdDeviation", this.config.shadowSize);
gaussFilter.appendChild(feGaussianBlur);
// composite 1
var feComposite1 = document.createElementNS(svg, "feComposite");
feComposite1.setAttribute("operator", "out");
feComposite1.setAttribute("in", "SourceGraphic");
feComposite1.setAttribute("in2", "offset-blur");
feComposite1.setAttribute("result", "inverse");
gaussFilter.appendChild(feComposite1);
// flood
var feFlood = document.createElementNS(svg, "feFlood");
feFlood.setAttribute("flood-color", "black");
feFlood.setAttribute("flood-opacity", this.config.shadowOpacity);
feFlood.setAttribute("result", "color");
gaussFilter.appendChild(feFlood);
// composite 2
var feComposite2 = document.createElementNS(svg, "feComposite");
feComposite2.setAttribute("operator", "in");
feComposite2.setAttribute("in", "color");
feComposite2.setAttribute("in2", "inverse");
feComposite2.setAttribute("result", "shadow");
gaussFilter.appendChild(feComposite2);
// composite 3
var feComposite3 = document.createElementNS(svg, "feComposite");
feComposite3.setAttribute("operator", "over");
feComposite3.setAttribute("in", "shadow");
feComposite3.setAttribute("in2", "SourceGraphic");
gaussFilter.appendChild(feComposite3);
// set shadow
if(this.config.showInnerShadow == true)
{
this.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
this.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
}
}
var getColorForPercentage = function(pct, col, grad)
{
var no = col.length;
if(no === 1) return col[0];
var inc = (grad) ? (1 / (no - 1)) : (1 / no);
var colors = new Array();
for(var i = 0; i < col.length; i++)
{
var percentage = (grad) ? (inc * i) : (inc * (i + 1));
var rval = parseInt((cutHex(col[i])).substring(0, 2), 16);
var gval = parseInt((cutHex(col[i])).substring(2, 4), 16);
var bval = parseInt((cutHex(col[i])).substring(4, 6), 16);
colors[i] = { pct: percentage, color: { r: rval, g: gval, b: bval } };
}
if(pct == 0) return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')';
for(var i = 0; i < colors.length; i++)
{
if(pct <= colors[i].pct)
{
if(grad == true)
{
var lower = colors[i - 1];
var upper = colors[i];
var range = upper.pct - lower.pct;
var rangePct = (pct - lower.pct) / range;
var pctLower = 1 - rangePct;
var pctUpper = rangePct;
var color = {
r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
};
return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
}
else
{
return 'rgb(' + [colors[i].color.r, colors[i].color.g, colors[i].color.b].join(',') + ')';
}
}
}
}
function getRandomInt(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function cutHex(str)
{
return (str.charAt(0) == "#") ? str.substring(1, 7) : str
}
function getStyle(oElm, strCssRule)
{
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle)
{
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle)
{
strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1)
{
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
function onCreateElementNsReady(func)
{
if(document.createElementNS != undefined)
{
func();
}
else
{
setTimeout(function()
{
onCreateElementNsReady(func);
}, 100);
}
}
// ----------------------------------------------------------
// A short snippet for detecting versions of IE in JavaScript
// without resorting to user-agent sniffing
// ----------------------------------------------------------
// If you're not in IE (or IE version is less than 5) then:
// ie === undefined
// If you're in IE (>=5) then you can determine which version:
// ie === 7; // IE7
// Thus, to detect IE:
// if (ie) {}
// And to detect the version:
// ie === 6 // IE6
// ie > 7 // IE8, IE9 ...
// ie < 9 // Anything less than IE9
// ----------------------------------------------------------
// UPDATE: Now using Live NodeList idea from @jdalton
var ie = (function()
{
var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i');
while(div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0]);
return v > 4 ? v : undef;
}());