/** * 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) && !v.jquery ) { 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; })();