| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 | 
							- /* Flot plugin for drawing all elements of a plot on the canvas.
 
- Copyright (c) 2007-2013 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 = $("<div> </div>")
 
- 						.css("position", "absolute")
 
- 						.addClass(typeof font === "string" ? font : null)
 
- 						.appendTo(this.getTextLayer(layer));
 
- 					font = {
 
- 						lineHeight: element.height(),
 
- 						style: element.css("font-style"),
 
- 						variant: element.css("font-variant"),
 
- 						weight: element.css("font-weight"),
 
- 						family: element.css("font-family"),
 
- 						color: element.css("color")
 
- 					};
 
- 					// Setting line-height to 1, without units, sets it equal
 
- 					// to the font-size, even if the font-size is abstract,
 
- 					// like 'smaller'.  This enables us to read the real size
 
- 					// via the element's height, working around browsers that
 
- 					// return the literal 'smaller' value.
 
- 					font.size = element.css("line-height", 1).height();
 
- 					element.remove();
 
- 				}
 
- 				textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;
 
- 				// Create a new info object, initializing the dimensions to
 
- 				// zero so we can count them up line-by-line.
 
- 				info = styleCache[text] = {
 
- 					width: 0,
 
- 					height: 0,
 
- 					positions: [],
 
- 					lines: [],
 
- 					font: {
 
- 						definition: textStyle,
 
- 						color: font.color
 
- 					}
 
- 				};
 
- 				context.save();
 
- 				context.font = textStyle;
 
- 				// Canvas can't handle multi-line strings; break on various
 
- 				// newlines, including HTML brs, to build a list of lines.
 
- 				// Note that we could split directly on regexps, but IE < 9 is
 
- 				// broken; revisit when we drop IE 7/8 support.
 
- 				var lines = (text + "").replace(/<br ?\/?>|\r\n|\r/g, "\n").split("\n");
 
- 				for (var i = 0; i < lines.length; ++i) {
 
- 					var lineText = lines[i],
 
- 						measured = context.measureText(lineText);
 
- 					info.width = Math.max(measured.width, info.width);
 
- 					info.height += font.lineHeight;
 
- 					info.lines.push({
 
- 						text: lineText,
 
- 						width: measured.width,
 
- 						height: font.lineHeight
 
- 					});
 
- 				}
 
- 				context.restore();
 
- 			}
 
- 			return info;
 
- 		};
 
- 		// Adds a text string to the canvas text overlay.
 
- 		Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) {
 
- 			if (!plot.getOptions().canvas) {
 
- 				return addText.call(this, layer, x, y, text, font, angle, width, halign, valign);
 
- 			}
 
- 			var info = this.getTextInfo(layer, text, font, angle, width),
 
- 				positions = info.positions,
 
- 				lines = info.lines;
 
- 			// Text is drawn with baseline 'middle', which we need to account
 
- 			// for by adding half a line's height to the y position.
 
- 			y += info.height / lines.length / 2;
 
- 			// Tweak the initial y-position to match vertical alignment
 
- 			if (valign == "middle") {
 
- 				y = Math.round(y - info.height / 2);
 
- 			} else if (valign == "bottom") {
 
- 				y = Math.round(y - info.height);
 
- 			} else {
 
- 				y = Math.round(y);
 
- 			}
 
- 			// FIXME: LEGACY BROWSER FIX
 
- 			// AFFECTS: Opera < 12.00
 
- 			// Offset the y coordinate, since Opera is off pretty
 
- 			// consistently compared to the other browsers.
 
- 			if (!!(window.opera && window.opera.version().split(".")[0] < 12)) {
 
- 				y -= 2;
 
- 			}
 
- 			// Determine whether this text already exists at this position.
 
- 			// If so, mark it for inclusion in the next render pass.
 
- 			for (var i = 0, position; position = positions[i]; i++) {
 
- 				if (position.x == x && position.y == y) {
 
- 					position.active = true;
 
- 					return;
 
- 				}
 
- 			}
 
- 			// If the text doesn't exist at this position, create a new entry
 
- 			position = {
 
- 				active: true,
 
- 				lines: [],
 
- 				x: x,
 
- 				y: y
 
- 			};
 
- 			positions.push(position);
 
- 			// Fill in the x & y positions of each line, adjusting them
 
- 			// individually for horizontal alignment.
 
- 			for (var i = 0, line; line = lines[i]; i++) {
 
- 				if (halign == "center") {
 
- 					position.lines.push([Math.round(x - line.width / 2), y]);
 
- 				} else if (halign == "right") {
 
- 					position.lines.push([Math.round(x - line.width), y]);
 
- 				} else {
 
- 					position.lines.push([Math.round(x), y]);
 
- 				}
 
- 				y += line.height;
 
- 			}
 
- 		};
 
- 	}
 
- 	$.plot.plugins.push({
 
- 		init: init,
 
- 		options: options,
 
- 		name: "canvas",
 
- 		version: "1.0"
 
- 	});
 
- })(jQuery);
 
 
  |