--- a/js/flotr2/js/Flotr.js +++ b/js/flotr2/js/Flotr.js @@ -1,1 +1,255 @@ - +/** + * 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; + +})(); +