251 lines
7.4 KiB
JavaScript
251 lines
7.4 KiB
JavaScript
/**
|
|
* Flotr2 (c) 2012 Carl Sutherland
|
|
* MIT License
|
|
* Special thanks to:
|
|
* Flotr: http://code.google.com/p/flotr/ (fork)
|
|
* Flot: https://github.com/flot/flot (original fork)
|
|
*/
|
|
(function () {
|
|
|
|
var
|
|
global = this,
|
|
previousFlotr = this.Flotr,
|
|
Flotr;
|
|
|
|
Flotr = {
|
|
_: _,
|
|
bean: bean,
|
|
isIphone: /iphone/i.test(navigator.userAgent),
|
|
isIE: (navigator.appVersion.indexOf("MSIE") != -1 ? parseFloat(navigator.appVersion.split("MSIE")[1]) : false),
|
|
|
|
/**
|
|
* An object of the registered graph types. Use Flotr.addType(type, object)
|
|
* to add your own type.
|
|
*/
|
|
graphTypes: {},
|
|
|
|
/**
|
|
* The list of the registered plugins
|
|
*/
|
|
plugins: {},
|
|
|
|
/**
|
|
* Can be used to add your own chart type.
|
|
* @param {String} name - Type of chart, like 'pies', 'bars' etc.
|
|
* @param {String} graphType - The object containing the basic drawing functions (draw, etc)
|
|
*/
|
|
addType: function(name, graphType){
|
|
Flotr.graphTypes[name] = graphType;
|
|
Flotr.defaultOptions[name] = graphType.options || {};
|
|
Flotr.defaultOptions.defaultType = Flotr.defaultOptions.defaultType || name;
|
|
},
|
|
|
|
/**
|
|
* Can be used to add a plugin
|
|
* @param {String} name - The name of the plugin
|
|
* @param {String} plugin - The object containing the plugin's data (callbacks, options, function1, function2, ...)
|
|
*/
|
|
addPlugin: function(name, plugin){
|
|
Flotr.plugins[name] = plugin;
|
|
Flotr.defaultOptions[name] = plugin.options || {};
|
|
},
|
|
|
|
/**
|
|
* Draws the graph. This function is here for backwards compatibility with Flotr version 0.1.0alpha.
|
|
* You could also draw graphs by directly calling Flotr.Graph(element, data, options).
|
|
* @param {Element} el - element to insert the graph into
|
|
* @param {Object} data - an array or object of dataseries
|
|
* @param {Object} options - an object containing options
|
|
* @param {Class} _GraphKlass_ - (optional) Class to pass the arguments to, defaults to Flotr.Graph
|
|
* @return {Object} returns a new graph object and of course draws the graph.
|
|
*/
|
|
draw: function(el, data, options, GraphKlass){
|
|
GraphKlass = GraphKlass || Flotr.Graph;
|
|
return new GraphKlass(el, data, options);
|
|
},
|
|
|
|
/**
|
|
* Recursively merges two objects.
|
|
* @param {Object} src - source object (likely the object with the least properties)
|
|
* @param {Object} dest - destination object (optional, object with the most properties)
|
|
* @return {Object} recursively merged Object
|
|
* @TODO See if we can't remove this.
|
|
*/
|
|
merge: function(src, dest){
|
|
var i, v, result = dest || {};
|
|
|
|
for (i in src) {
|
|
v = src[i];
|
|
if (v && typeof(v) === 'object') {
|
|
if (v.constructor === Array) {
|
|
result[i] = this._.clone(v);
|
|
} else if (v.constructor !== RegExp && !this._.isElement(v)) {
|
|
result[i] = Flotr.merge(v, (dest ? dest[i] : undefined));
|
|
} else {
|
|
result[i] = v;
|
|
}
|
|
} else {
|
|
result[i] = v;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* Recursively clones an object.
|
|
* @param {Object} object - The object to clone
|
|
* @return {Object} the clone
|
|
* @TODO See if we can't remove this.
|
|
*/
|
|
clone: function(object){
|
|
return Flotr.merge(object, {});
|
|
},
|
|
|
|
/**
|
|
* Function calculates the ticksize and returns it.
|
|
* @param {Integer} noTicks - number of ticks
|
|
* @param {Integer} min - lower bound integer value for the current axis
|
|
* @param {Integer} max - upper bound integer value for the current axis
|
|
* @param {Integer} decimals - number of decimals for the ticks
|
|
* @return {Integer} returns the ticksize in pixels
|
|
*/
|
|
getTickSize: function(noTicks, min, max, decimals){
|
|
var delta = (max - min) / noTicks,
|
|
magn = Flotr.getMagnitude(delta),
|
|
tickSize = 10,
|
|
norm = delta / magn; // Norm is between 1.0 and 10.0.
|
|
|
|
if(norm < 1.5) tickSize = 1;
|
|
else if(norm < 2.25) tickSize = 2;
|
|
else if(norm < 3) tickSize = ((decimals === 0) ? 2 : 2.5);
|
|
else if(norm < 7.5) tickSize = 5;
|
|
|
|
return tickSize * magn;
|
|
},
|
|
|
|
/**
|
|
* Default tick formatter.
|
|
* @param {String, Integer} val - tick value integer
|
|
* @param {Object} axisOpts - the axis' options
|
|
* @return {String} formatted tick string
|
|
*/
|
|
defaultTickFormatter: function(val, axisOpts){
|
|
return val+'';
|
|
},
|
|
|
|
/**
|
|
* Formats the mouse tracker values.
|
|
* @param {Object} obj - Track value Object {x:..,y:..}
|
|
* @return {String} Formatted track string
|
|
*/
|
|
defaultTrackFormatter: function(obj){
|
|
return '('+obj.x+', '+obj.y+')';
|
|
},
|
|
|
|
/**
|
|
* Utility function to convert file size values in bytes to kB, MB, ...
|
|
* @param value {Number} - The value to convert
|
|
* @param precision {Number} - The number of digits after the comma (default: 2)
|
|
* @param base {Number} - The base (default: 1000)
|
|
*/
|
|
engineeringNotation: function(value, precision, base){
|
|
var sizes = ['Y','Z','E','P','T','G','M','k',''],
|
|
fractionSizes = ['y','z','a','f','p','n','µ','m',''],
|
|
total = sizes.length;
|
|
|
|
base = base || 1000;
|
|
precision = Math.pow(10, precision || 2);
|
|
|
|
if (value === 0) return 0;
|
|
|
|
if (value > 1) {
|
|
while (total-- && (value >= base)) value /= base;
|
|
}
|
|
else {
|
|
sizes = fractionSizes;
|
|
total = sizes.length;
|
|
while (total-- && (value < 1)) value *= base;
|
|
}
|
|
|
|
return (Math.round(value * precision) / precision) + sizes[total];
|
|
},
|
|
|
|
/**
|
|
* Returns the magnitude of the input value.
|
|
* @param {Integer, Float} x - integer or float value
|
|
* @return {Integer, Float} returns the magnitude of the input value
|
|
*/
|
|
getMagnitude: function(x){
|
|
return Math.pow(10, Math.floor(Math.log(x) / Math.LN10));
|
|
},
|
|
toPixel: function(val){
|
|
return Math.floor(val)+0.5;//((val-Math.round(val) < 0.4) ? (Math.floor(val)-0.5) : val);
|
|
},
|
|
toRad: function(angle){
|
|
return -angle * (Math.PI/180);
|
|
},
|
|
floorInBase: function(n, base) {
|
|
return base * Math.floor(n / base);
|
|
},
|
|
drawText: function(ctx, text, x, y, style) {
|
|
if (!ctx.fillText) {
|
|
ctx.drawText(text, x, y, style);
|
|
return;
|
|
}
|
|
|
|
style = this._.extend({
|
|
size: Flotr.defaultOptions.fontSize,
|
|
color: '#000000',
|
|
textAlign: 'left',
|
|
textBaseline: 'bottom',
|
|
weight: 1,
|
|
angle: 0
|
|
}, style);
|
|
|
|
ctx.save();
|
|
ctx.translate(x, y);
|
|
ctx.rotate(style.angle);
|
|
ctx.fillStyle = style.color;
|
|
ctx.font = (style.weight > 1 ? "bold " : "") + (style.size*1.3) + "px sans-serif";
|
|
ctx.textAlign = style.textAlign;
|
|
ctx.textBaseline = style.textBaseline;
|
|
ctx.fillText(text, 0, 0);
|
|
ctx.restore();
|
|
},
|
|
getBestTextAlign: function(angle, style) {
|
|
style = style || {textAlign: 'center', textBaseline: 'middle'};
|
|
angle += Flotr.getTextAngleFromAlign(style);
|
|
|
|
if (Math.abs(Math.cos(angle)) > 10e-3)
|
|
style.textAlign = (Math.cos(angle) > 0 ? 'right' : 'left');
|
|
|
|
if (Math.abs(Math.sin(angle)) > 10e-3)
|
|
style.textBaseline = (Math.sin(angle) > 0 ? 'top' : 'bottom');
|
|
|
|
return style;
|
|
},
|
|
alignTable: {
|
|
'right middle' : 0,
|
|
'right top' : Math.PI/4,
|
|
'center top' : Math.PI/2,
|
|
'left top' : 3*(Math.PI/4),
|
|
'left middle' : Math.PI,
|
|
'left bottom' : -3*(Math.PI/4),
|
|
'center bottom': -Math.PI/2,
|
|
'right bottom' : -Math.PI/4,
|
|
'center middle': 0
|
|
},
|
|
getTextAngleFromAlign: function(style) {
|
|
return Flotr.alignTable[style.textAlign+' '+style.textBaseline] || 0;
|
|
},
|
|
noConflict : function () {
|
|
global.Flotr = previousFlotr;
|
|
return this;
|
|
}
|
|
};
|
|
|
|
global.Flotr = Flotr;
|
|
|
|
})();
|