/* Flot plugin for drawing all elements of a plot on the canvas. Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. Flot normally produces certain elements, like axis labels and the legend, using HTML elements. This permits greater interactivity and customization, and often looks better, due to cross-browser canvas text inconsistencies and limitations. It can also be desirable to render the plot entirely in canvas, particularly if the goal is to save it as an image, or if Flot is being used in a context where the HTML DOM does not exist, as is the case within Node.js. This plugin switches out Flot's standard drawing operations for canvas-only replacements. Currently the plugin supports only axis labels, but it will eventually allow every element of the plot to be rendered directly to canvas. The plugin supports these options: { canvas: boolean } The "canvas" option controls whether full canvas drawing is enabled, making it possible to toggle on and off. This is useful when a plot uses HTML text in the browser, but needs to redraw with canvas text when exporting as an image. */ (function($) { var options = { canvas: true }; var render, getTextInfo, addText; // Cache the prototype hasOwnProperty for faster access var hasOwnProperty = Object.prototype.hasOwnProperty; function init(plot, classes) { var Canvas = classes.Canvas; // We only want to replace the functions once; the second time around // we would just get our new function back. This whole replacing of // prototype functions is a disaster, and needs to be changed ASAP. if (render == null) { getTextInfo = Canvas.prototype.getTextInfo, addText = Canvas.prototype.addText, render = Canvas.prototype.render; } // Finishes rendering the canvas, including overlaid text Canvas.prototype.render = function() { if (!plot.getOptions().canvas) { return render.call(this); } var context = this.context, cache = this._textCache; // For each text layer, render elements marked as active context.save(); context.textBaseline = "middle"; for (var layerKey in cache) { if (hasOwnProperty.call(cache, layerKey)) { var layerCache = cache[layerKey]; for (var styleKey in layerCache) { if (hasOwnProperty.call(layerCache, styleKey)) { var styleCache = layerCache[styleKey], updateStyles = true; for (var key in styleCache) { if (hasOwnProperty.call(styleCache, key)) { var info = styleCache[key], positions = info.positions, lines = info.lines; // Since every element at this level of the cache have the // same font and fill styles, we can just change them once // using the values from the first element. if (updateStyles) { context.fillStyle = info.font.color; context.font = info.font.definition; updateStyles = false; } for (var i = 0, position; position = positions[i]; i++) { if (position.active) { for (var j = 0, line; line = position.lines[j]; j++) { context.fillText(lines[j].text, line[0], line[1]); } } else { positions.splice(i--, 1); } } if (positions.length == 0) { delete styleCache[key]; } } } } } } } context.restore(); }; // Creates (if necessary) and returns a text info object. // // When the canvas option is set, the object looks like this: // // { // width: Width of the text's bounding box. // height: Height of the text's bounding box. // positions: Array of positions at which this text is drawn. // lines: [{ // height: Height of this line. // widths: Width of this line. // text: Text on this line. // }], // font: { // definition: Canvas font property string. // color: Color of the text. // }, // } // // The positions array contains objects that look like this: // // { // active: Flag indicating whether the text should be visible. // lines: Array of [x, y] coordinates at which to draw the line. // x: X coordinate at which to draw the text. // y: Y coordinate at which to draw the text. // } Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { if (!plot.getOptions().canvas) { return getTextInfo.call(this, layer, text, font, angle, width); } var textStyle, layerCache, styleCache, info; // Cast the value to a string, in case we were given a number text = "" + text; // If the font is a font-spec object, generate a CSS definition if (typeof font === "object") { textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; } else { textStyle = font; } // Retrieve (or create) the cache for the text's layer and styles layerCache = this._textCache[layer]; if (layerCache == null) { layerCache = this._textCache[layer] = {}; } styleCache = layerCache[textStyle]; if (styleCache == null) { styleCache = layerCache[textStyle] = {}; } info = styleCache[text]; if (info == null) { var context = this.context; // If the font was provided as CSS, create a div with those // classes and examine it to generate a canvas font spec. if (typeof font !== "object") { var element = $("