/*jslint vars: true, nomen: true, plusplus: true, maxerr: 500, indent: 4 */ /** * @license * Copyright 2011 Juan Manuel Caicedo Carvajal (juan@cavorite.com) * MIT-licensed (http://opensource.org/licenses/MIT) */ /** * @fileoverview This file contains additional features for dygraphs, which * are not required but can be useful for certain use cases. Examples include * exporting a dygraph as a PNG image. */ /** * Demo code for exporting a Dygraph object as an image. * * See: http://cavorite.com/labs/js/dygraphs-export/ */ Dygraph.Export = {}; Dygraph.Export.DEFAULT_ATTRS = { backgroundColor: "transparent", //Texts displayed below the chart's x-axis and to the left of the y-axis titleFont: "bold 18px serif", titleFontColor: "black", //Texts displayed below the chart's x-axis and to the left of the y-axis axisLabelFont: "bold 14px serif", axisLabelFontColor: "black", // Texts for the axis ticks labelFont: "normal 12px serif", labelFontColor: "black", // Text for the chart legend legendFont: "bold 12px serif", legendFontColor: "black", // Default position for vertical labels vLabelLeft: 20, legendHeight: 20, // Height of the legend area legendMargin: 20, lineHeight: 30, maxlabelsWidth: 0, labelTopMargin: 35, magicNumbertop: 8 }; /** * Tests whether the browser supports the canvas API and its methods for * drawing text and exporting it as a data URL. */ Dygraph.Export.isSupported = function () { "use strict"; try { var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); return (!!canvas.toDataURL && !!context.fillText); } catch (e) { // Silent exception. } return false; }; /** * Exports a dygraph object as a PNG image. * * dygraph: A Dygraph object * img: An IMG DOM node * userOptions: An object with the user specified options. * */ Dygraph.Export.asPNG = function (dygraph, img, userOptions) { "use strict"; var canvas = Dygraph.Export.asCanvas(dygraph, userOptions); img.src = canvas.toDataURL(); }; /** * Exports a dygraph into a single canvas object. * * Returns a canvas object that can be exported as a PNG. * * dygraph: A Dygraph object * userOptions: An object with the user specified options. * */ Dygraph.Export.asCanvas = function (dygraph, userOptions) { "use strict"; var options = {}, canvas = Dygraph.createCanvas(); Dygraph.update(options, Dygraph.Export.DEFAULT_ATTRS); Dygraph.update(options, userOptions); canvas.width = dygraph.width_; canvas.height = dygraph.height_ + options.legendHeight; Dygraph.Export.drawPlot(canvas, dygraph, options); Dygraph.Export.drawLegend(canvas, dygraph, options); return canvas; }; /** * Adds the plot and the axes to a canvas context. */ Dygraph.Export.drawPlot = function (canvas, dygraph, options) { "use strict"; var ctx = canvas.getContext("2d"); // Add user defined background ctx.fillStyle = options.backgroundColor; ctx.fillRect(0, 0, canvas.width, canvas.height); // Copy the plot canvas into the context of the new image. var plotCanvas = dygraph.hidden_; var i = 0; ctx.drawImage(plotCanvas, 0, 0); // Add the x and y axes var axesPluginDict = Dygraph.Export.getPlugin(dygraph, 'Axes Plugin'); if (axesPluginDict) { var axesPlugin = axesPluginDict.plugin; for (i = 0; i < axesPlugin.ylabels_.length; i++) { Dygraph.Export.putLabel(ctx, axesPlugin.ylabels_[i], options, options.labelFont, options.labelFontColor); } for (i = 0; i < axesPlugin.xlabels_.length; i++) { Dygraph.Export.putLabel(ctx, axesPlugin.xlabels_[i], options, options.labelFont, options.labelFontColor); } } // Title and axis labels var labelsPluginDict = Dygraph.Export.getPlugin(dygraph, 'ChartLabels Plugin'); if (labelsPluginDict) { var labelsPlugin = labelsPluginDict.plugin; Dygraph.Export.putLabel(ctx, labelsPlugin.title_div_, options, options.titleFont, options.titleFontColor); Dygraph.Export.putLabel(ctx, labelsPlugin.xlabel_div_, options, options.axisLabelFont, options.axisLabelFontColor); Dygraph.Export.putVerticalLabelY1(ctx, labelsPlugin.ylabel_div_, options, options.axisLabelFont, options.axisLabelFontColor, "center"); Dygraph.Export.putVerticalLabelY2(ctx, labelsPlugin.y2label_div_, options, options.axisLabelFont, options.axisLabelFontColor, "center"); } for (i = 0; i < dygraph.layout_.annotations.length; i++) { Dygraph.Export.putLabelAnn(ctx, dygraph.layout_.annotations[i], options, options.labelFont, options.labelColor); } }; /** * Draws a label (axis label or graph title) at the same position * where the div containing the text is located. */ Dygraph.Export.putLabel = function (ctx, divLabel, options, font, color) { "use strict"; if (!divLabel || !divLabel.style) { return; } var top = parseInt(divLabel.style.top, 10); var left = parseInt(divLabel.style.left, 10); if (!divLabel.style.top.length) { var bottom = parseInt(divLabel.style.bottom, 10); var height = parseInt(divLabel.style.height, 10); top = ctx.canvas.height - options.legendHeight - bottom - height; } // FIXME: Remove this 'magic' number needed to get the line-height. top = top + options.magicNumbertop; var width = parseInt(divLabel.style.width, 10); switch (divLabel.style.textAlign) { case "center": left = left + Math.ceil(width / 2); break; case "right": left = left + width; break; } Dygraph.Export.putText(ctx, left, top, divLabel, font, color); }; /** * Draws a label Y1 rotated 90 degrees counterclockwise. */ Dygraph.Export.putVerticalLabelY1 = function (ctx, divLabel, options, font, color, textAlign) { "use strict"; if (!divLabel) { return; } var top = parseInt(divLabel.style.top, 10); var left = parseInt(divLabel.style.left, 10) + parseInt(divLabel.style.width, 10) / 2; var text = divLabel.innerText || divLabel.textContent; // FIXME: The value of the 'left' property is frequently 0, used the option. if (!left) left = options.vLabelLeft; if (textAlign == "center") { var textDim = ctx.measureText(text); top = Math.ceil((ctx.canvas.height - textDim.width) / 2 + textDim.width); } ctx.save(); ctx.translate(0, ctx.canvas.height); ctx.rotate(-Math.PI / 2); ctx.fillStyle = color; ctx.font = font; ctx.textAlign = textAlign; ctx.fillText(text, top, left); ctx.restore(); }; /** * Draws a label Y2 rotated 90 degrees clockwise. */ Dygraph.Export.putVerticalLabelY2 = function (ctx, divLabel, options, font, color, textAlign) { "use strict"; if (!divLabel) { return; } var top = parseInt(divLabel.style.top, 10); var right = parseInt(divLabel.style.right, 10) + parseInt(divLabel.style.width, 10) * 2; var text = divLabel.innerText || divLabel.textContent; if (textAlign == "center") { top = Math.ceil(ctx.canvas.height / 2); } ctx.save(); ctx.translate(parseInt(divLabel.style.width, 10), 0); ctx.rotate(Math.PI / 2); ctx.fillStyle = color; ctx.font = font; ctx.textAlign = textAlign; ctx.fillText(text, top, right - ctx.canvas.width); ctx.restore(); }; /** * Draws the text contained in 'divLabel' at the specified position. */ Dygraph.Export.putText = function (ctx, left, top, divLabel, font, color) { "use strict"; var textAlign = divLabel.style.textAlign || "left"; var text = divLabel.innerText || divLabel.textContent; ctx.fillStyle = color; ctx.font = font; ctx.textAlign = textAlign; ctx.textBaseline = "middle"; ctx.fillText(text, left, top); }; /** * Draws the legend of a dygraph * */ Dygraph.Export.drawLegend = function (canvas, dygraph, options) { "use strict"; var ctx = canvas.getContext("2d"); // Margin from the plot var labelTopMargin = 10; // Margin between labels var labelMargin = 5; var colors = dygraph.getColors(); // Drop the first element, which is the label for the time dimension var labels = dygraph.attr_("labels").slice(1); // 1. Compute the width of the labels: var labelsWidth = 0; var i; for (i = 0; i < labels.length; i++) { labelsWidth = labelsWidth + ctx.measureText("- " + labels[i]).width + labelMargin; } var labelsX = Math.floor((canvas.width - labelsWidth) / 2); var labelsY = canvas.height - options.legendHeight + labelTopMargin; var labelVisibility=dygraph.attr_("visibility"); ctx.font = options.legendFont; ctx.textAlign = "left"; ctx.textBaseline = "middle"; var usedColorCount=0; for (i = 0; i < labels.length; i++) { if (labelVisibility[i]) { //TODO Replace the minus sign by a proper dash, although there is a // problem when the page encoding is different than the encoding // of this file (UTF-8). var txt = "- " + labels[i]; ctx.fillStyle = colors[usedColorCount]; usedColorCount++ ctx.fillText(txt, labelsX, labelsY); labelsX = labelsX + ctx.measureText(txt).width + labelMargin; } } }; /** * Finds a plugin by the value returned by its toString method.. * * Returns the the dictionary corresponding to the plugin for the argument. * If the plugin is not found, it returns null. */ Dygraph.Export.getPlugin = function(dygraph, name) { for (i = 0; i < dygraph.plugins_.length; i++) { if (dygraph.plugins_[i].plugin.toString() == name) { return dygraph.plugins_[i]; } } return null; }