mirror of https://github.com/ColorlibHQ/AdminLTE
				
				
				
			
		
			
				
	
	
		
			346 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			346 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
/* 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);
 |