text: (window["webpackJsonp"] = window["webpackJsonp"] || []).push([["/js/flot"],{
  
  /***/ "./node_modules/flot/jquery.flot.categories.js":
  /*!*****************************************************!*\
    !*** ./node_modules/flot/jquery.flot.categories.js ***!
    \*****************************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/* Flot plugin for plotting textual data or categories.
  
  Copyright (c) 2007-2014 IOLA and Ole Laursen.
  Licensed under the MIT license.
  
  Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin
  allows you to plot such a dataset directly.
  
  To enable it, you must specify mode: "categories" on the axis with the textual
  labels, e.g.
  
  	$.plot("#placeholder", data, { xaxis: { mode: "categories" } });
  
  By default, the labels are ordered as they are met in the data series. If you
  need a different ordering, you can specify "categories" on the axis options
  and list the categories there:
  
  	xaxis: {
  		mode: "categories",
  		categories: ["February", "March", "April"]
  	}
  
  If you need to customize the distances between the categories, you can specify
  "categories" as an object mapping labels to values
  
  	xaxis: {
  		mode: "categories",
  		categories: { "February": 1, "March": 3, "April": 4 }
  	}
  
  If you don't specify all categories, the remaining categories will be numbered
  from the max value plus 1 (with a spacing of 1 between each).
  
  Internally, the plugin works by transforming the input data through an auto-
  generated mapping where the first category becomes 0, the second 1, etc.
  Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this
  is visible in hover and click events that return numbers rather than the
  category labels). The plugin also overrides the tick generator to spit out the
  categories as ticks instead of the values.
  
  If you need to map a value back to its label, the mapping is always accessible
  as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories.
  
  */
  
  (function ($) {
      var options = {
          xaxis: {
              categories: null
          },
          yaxis: {
              categories: null
          }
      };
      
      function processRawData(plot, series, data, datapoints) {
          // if categories are enabled, we need to disable
          // auto-transformation to numbers so the strings are intact
          // for later processing
  
          var xCategories = series.xaxis.options.mode == "categories",
              yCategories = series.yaxis.options.mode == "categories";
          
          if (!(xCategories || yCategories))
              return;
  
          var format = datapoints.format;
  
          if (!format) {
              // FIXME: auto-detection should really not be defined here
              var s = series;
              format = [];
              format.push({ x: true, number: true, required: true });
              format.push({ y: true, number: true, required: true });
  
              if (s.bars.show || (s.lines.show && s.lines.fill)) {
                  var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
                  format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale });
                  if (s.bars.horizontal) {
                      delete format[format.length - 1].y;
                      format[format.length - 1].x = true;
                  }
              }
              
              datapoints.format = format;
          }
  
          for (var m = 0; m < format.length; ++m) {
              if (format[m].x && xCategories)
                  format[m].number = false;
              
              if (format[m].y && yCategories)
                  format[m].number = false;
          }
      }
  
      function getNextIndex(categories) {
          var index = -1;
          
          for (var v in categories)
              if (categories[v] > index)
                  index = categories[v];
  
          return index + 1;
      }
  
      function categoriesTickGenerator(axis) {
          var res = [];
          for (var label in axis.categories) {
              var v = axis.categories[label];
              if (v >= axis.min && v <= axis.max)
                  res.push([v, label]);
          }
  
          res.sort(function (a, b) { return a[0] - b[0]; });
  
          return res;
      }
      
      function setupCategoriesForAxis(series, axis, datapoints) {
          if (series[axis].options.mode != "categories")
              return;
          
          if (!series[axis].categories) {
              // parse options
              var c = {}, o = series[axis].options.categories || {};
              if ($.isArray(o)) {
                  for (var i = 0; i < o.length; ++i)
                      c[o[i]] = i;
              }
              else {
                  for (var v in o)
                      c[v] = o[v];
              }
              
              series[axis].categories = c;
          }
  
          // fix ticks
          if (!series[axis].options.ticks)
              series[axis].options.ticks = categoriesTickGenerator;
  
          transformPointsOnAxis(datapoints, axis, series[axis].categories);
      }
      
      function transformPointsOnAxis(datapoints, axis, categories) {
          // go through the points, transforming them
          var points = datapoints.points,
              ps = datapoints.pointsize,
              format = datapoints.format,
              formatColumn = axis.charAt(0),
              index = getNextIndex(categories);
  
          for (var i = 0; i < points.length; i += ps) {
              if (points[i] == null)
                  continue;
              
              for (var m = 0; m < ps; ++m) {
                  var val = points[i + m];
  
                  if (val == null || !format[m][formatColumn])
                      continue;
  
                  if (!(val in categories)) {
                      categories[val] = index;
                      ++index;
                  }
                  
                  points[i + m] = categories[val];
              }
          }
      }
  
      function processDatapoints(plot, series, datapoints) {
          setupCategoriesForAxis(series, "xaxis", datapoints);
          setupCategoriesForAxis(series, "yaxis", datapoints);
      }
  
      function init(plot) {
          plot.hooks.processRawData.push(processRawData);
          plot.hooks.processDatapoints.push(processDatapoints);
      }
      
      $.plot.plugins.push({
          init: init,
          options: options,
          name: 'categories',
          version: '1.0'
      });
  })(jQuery);
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/flot/jquery.flot.js":
  /*!******************************************!*\
    !*** ./node_modules/flot/jquery.flot.js ***!
    \******************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/* Javascript plotting library for jQuery, version 0.8.3.
  
  Copyright (c) 2007-2014 IOLA and Ole Laursen.
  Licensed under the MIT license.
  
  */
  
  // first an inline dependency, jquery.colorhelpers.js, we inline it here
  // for convenience
  
  /* Plugin for jQuery for working with colors.
   *
   * Version 1.1.
   *
   * Inspiration from jQuery color animation plugin by John Resig.
   *
   * Released under the MIT license by Ole Laursen, October 2009.
   *
   * Examples:
   *
   *   $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
   *   var c = $.color.extract($("#mydiv"), 'background-color');
   *   console.log(c.r, c.g, c.b, c.a);
   *   $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
   *
   * Note that .scale() and .add() return the same modified object
   * instead of making a new one.
   *
   * V. 1.1: Fix error handling so e.g. parsing an empty string does
   * produce a color rather than just crashing.
   */
  (function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
  
  // the actual Flot code
  (function($) {
  
  	// Cache the prototype hasOwnProperty for faster access
  
  	var hasOwnProperty = Object.prototype.hasOwnProperty;
  
      // A shim to provide 'detach' to jQuery versions prior to 1.4.  Using a DOM
      // operation produces the same effect as detach, i.e. removing the element
      // without touching its jQuery data.
  
      // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+.
  
      if (!$.fn.detach) {
          $.fn.detach = function() {
              return this.each(function() {
                  if (this.parentNode) {
                      this.parentNode.removeChild( this );
                  }
              });
          };
      }
  
  	///////////////////////////////////////////////////////////////////////////
  	// The Canvas object is a wrapper around an HTML5 <canvas> tag.
  	//
  	// @constructor
  	// @param {string} cls List of classes to apply to the canvas.
  	// @param {element} container Element onto which to append the canvas.
  	//
  	// Requiring a container is a little iffy, but unfortunately canvas
  	// operations don't work unless the canvas is attached to the DOM.
  
  	function Canvas(cls, container) {
  
  		var element = container.children("." + cls)[0];
  
  		if (element == null) {
  
  			element = document.createElement("canvas");
  			element.className = cls;
  
  			$(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 })
  				.appendTo(container);
  
  			// If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas
  
  			if (!element.getContext) {
  				if (window.G_vmlCanvasManager) {
  					element = window.G_vmlCanvasManager.initElement(element);
  				} else {
  					throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");
  				}
  			}
  		}
  
  		this.element = element;
  
  		var context = this.context = element.getContext("2d");
  
  		// Determine the screen's ratio of physical to device-independent
  		// pixels.  This is the ratio between the canvas width that the browser
  		// advertises and the number of pixels actually present in that space.
  
  		// The iPhone 4, for example, has a device-independent width of 320px,
  		// but its screen is actually 640px wide.  It therefore has a pixel
  		// ratio of 2, while most normal devices have a ratio of 1.
  
  		var devicePixelRatio = window.devicePixelRatio || 1,
  			backingStoreRatio =
  				context.webkitBackingStorePixelRatio ||
  				context.mozBackingStorePixelRatio ||
  				context.msBackingStorePixelRatio ||
  				context.oBackingStorePixelRatio ||
  				context.backingStorePixelRatio || 1;
  
  		this.pixelRatio = devicePixelRatio / backingStoreRatio;
  
  		// Size the canvas to match the internal dimensions of its container
  
  		this.resize(container.width(), container.height());
  
  		// Collection of HTML div layers for text overlaid onto the canvas
  
  		this.textContainer = null;
  		this.text = {};
  
  		// Cache of text fragments and metrics, so we can avoid expensively
  		// re-calculating them when the plot is re-rendered in a loop.
  
  		this._textCache = {};
  	}
  
  	// Resizes the canvas to the given dimensions.
  	//
  	// @param {number} width New width of the canvas, in pixels.
  	// @param {number} width New height of the canvas, in pixels.
  
  	Canvas.prototype.resize = function(width, height) {
  
  		if (width <= 0 || height <= 0) {
  			throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height);
  		}
  
  		var element = this.element,
  			context = this.context,
  			pixelRatio = this.pixelRatio;
  
  		// Resize the canvas, increasing its density based on the display's
  		// pixel ratio; basically giving it more pixels without increasing the
  		// size of its element, to take advantage of the fact that retina
  		// displays have that many more pixels in the same advertised space.
  
  		// Resizing should reset the state (excanvas seems to be buggy though)
  
  		if (this.width != width) {
  			element.width = width * pixelRatio;
  			element.style.width = width + "px";
  			this.width = width;
  		}
  
  		if (this.height != height) {
  			element.height = height * pixelRatio;
  			element.style.height = height + "px";
  			this.height = height;
  		}
  
  		// Save the context, so we can reset in case we get replotted.  The
  		// restore ensure that we're really back at the initial state, and
  		// should be safe even if we haven't saved the initial state yet.
  
  		context.restore();
  		context.save();
  
  		// Scale the coordinate space to match the display density; so even though we
  		// may have twice as many pixels, we still want lines and other drawing to
  		// appear at the same size; the extra pixels will just make them crisper.
  
  		context.scale(pixelRatio, pixelRatio);
  	};
  
  	// Clears the entire canvas area, not including any overlaid HTML text
  
  	Canvas.prototype.clear = function() {
  		this.context.clearRect(0, 0, this.width, this.height);
  	};
  
  	// Finishes rendering the canvas, including managing the text overlay.
  
  	Canvas.prototype.render = function() {
  
  		var cache = this._textCache;
  
  		// For each text layer, add elements marked as active that haven't
  		// already been rendered, and remove those that are no longer active.
  
  		for (var layerKey in cache) {
  			if (hasOwnProperty.call(cache, layerKey)) {
  
  				var layer = this.getTextLayer(layerKey),
  					layerCache = cache[layerKey];
  
  				layer.hide();
  
  				for (var styleKey in layerCache) {
  					if (hasOwnProperty.call(layerCache, styleKey)) {
  						var styleCache = layerCache[styleKey];
  						for (var key in styleCache) {
  							if (hasOwnProperty.call(styleCache, key)) {
  
  								var positions = styleCache[key].positions;
  
  								for (var i = 0, position; position = positions[i]; i++) {
  									if (position.active) {
  										if (!position.rendered) {
  											layer.append(position.element);
  											position.rendered = true;
  										}
  									} else {
  										positions.splice(i--, 1);
  										if (position.rendered) {
  											position.element.detach();
  										}
  									}
  								}
  
  								if (positions.length == 0) {
  									delete styleCache[key];
  								}
  							}
  						}
  					}
  				}
  
  				layer.show();
  			}
  		}
  	};
  
  	// Creates (if necessary) and returns the text overlay container.
  	//
  	// @param {string} classes String of space-separated CSS classes used to
  	//     uniquely identify the text layer.
  	// @return {object} The jQuery-wrapped text-layer div.
  
  	Canvas.prototype.getTextLayer = function(classes) {
  
  		var layer = this.text[classes];
  
  		// Create the text layer if it doesn't exist
  
  		if (layer == null) {
  
  			// Create the text layer container, if it doesn't exist
  
  			if (this.textContainer == null) {
  				this.textContainer = $("<div class='flot-text'></div>")
  					.css({
  						position: "absolute",
  						top: 0,
  						left: 0,
  						bottom: 0,
  						right: 0,
  						'font-size': "smaller",
  						color: "#545454"
  					})
  					.insertAfter(this.element);
  			}
  
  			layer = this.text[classes] = $("<div></div>")
  				.addClass(classes)
  				.css({
  					position: "absolute",
  					top: 0,
  					left: 0,
  					bottom: 0,
  					right: 0
  				})
  				.appendTo(this.textContainer);
  		}
  
  		return layer;
  	};
  
  	// Creates (if necessary) and returns a text info object.
  	//
  	// The object looks like this:
  	//
  	// {
  	//     width: Width of the text's wrapper div.
  	//     height: Height of the text's wrapper div.
  	//     element: The jQuery-wrapped HTML div containing the text.
  	//     positions: Array of positions at which this text is drawn.
  	// }
  	//
  	// The positions array contains objects that look like this:
  	//
  	// {
  	//     active: Flag indicating whether the text should be visible.
  	//     rendered: Flag indicating whether the text is currently visible.
  	//     element: The jQuery-wrapped HTML div containing the text.
  	//     x: X coordinate at which to draw the text.
  	//     y: Y coordinate at which to draw the text.
  	// }
  	//
  	// Each position after the first receives a clone of the original element.
  	//
  	// The idea is that that the width, height, and general 'identity' of the
  	// text is constant no matter where it is placed; the placements are a
  	// secondary property.
  	//
  	// Canvas maintains a cache of recently-used text info objects; getTextInfo
  	// either returns the cached element or creates a new entry.
  	//
  	// @param {string} layer A string of space-separated CSS classes uniquely
  	//     identifying the layer containing this text.
  	// @param {string} text Text string to retrieve info for.
  	// @param {(string|object)=} font Either a string of space-separated CSS
  	//     classes or a font-spec object, defining the text's font and style.
  	// @param {number=} angle Angle at which to rotate the text, in degrees.
  	//     Angle is currently unused, it will be implemented in the future.
  	// @param {number=} width Maximum width of the text before it wraps.
  	// @return {object} a text info object.
  
  	Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) {
  
  		var textStyle, layerCache, styleCache, info;
  
  		// Cast the value to a string, in case we were given a number or such
  
  		text = "" + text;
  
  		// If the font is a font-spec object, generate a CSS font definition
  
  		if (typeof font === "object") {
  			textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "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 we can't find a matching element in our cache, create a new one
  
  		if (info == null) {
  
  			var element = $("<div></div>").html(text)
  				.css({
  					position: "absolute",
  					'max-width': width,
  					top: -9999
  				})
  				.appendTo(this.getTextLayer(layer));
  
  			if (typeof font === "object") {
  				element.css({
  					font: textStyle,
  					color: font.color
  				});
  			} else if (typeof font === "string") {
  				element.addClass(font);
  			}
  
  			info = styleCache[text] = {
  				width: element.outerWidth(true),
  				height: element.outerHeight(true),
  				element: element,
  				positions: []
  			};
  
  			element.detach();
  		}
  
  		return info;
  	};
  
  	// Adds a text string to the canvas text overlay.
  	//
  	// The text isn't drawn immediately; it is marked as rendering, which will
  	// result in its addition to the canvas on the next render pass.
  	//
  	// @param {string} layer A string of space-separated CSS classes uniquely
  	//     identifying the layer containing this text.
  	// @param {number} x X coordinate at which to draw the text.
  	// @param {number} y Y coordinate at which to draw the text.
  	// @param {string} text Text string to draw.
  	// @param {(string|object)=} font Either a string of space-separated CSS
  	//     classes or a font-spec object, defining the text's font and style.
  	// @param {number=} angle Angle at which to rotate the text, in degrees.
  	//     Angle is currently unused, it will be implemented in the future.
  	// @param {number=} width Maximum width of the text before it wraps.
  	// @param {string=} halign Horizontal alignment of the text; either "left",
  	//     "center" or "right".
  	// @param {string=} valign Vertical alignment of the text; either "top",
  	//     "middle" or "bottom".
  
  	Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) {
  
  		var info = this.getTextInfo(layer, text, font, angle, width),
  			positions = info.positions;
  
  		// Tweak the div's position to match the text's alignment
  
  		if (halign == "center") {
  			x -= info.width / 2;
  		} else if (halign == "right") {
  			x -= info.width;
  		}
  
  		if (valign == "middle") {
  			y -= info.height / 2;
  		} else if (valign == "bottom") {
  			y -= info.height;
  		}
  
  		// 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
  
  		// For the very first position we'll re-use the original element,
  		// while for subsequent ones we'll clone it.
  
  		position = {
  			active: true,
  			rendered: false,
  			element: positions.length ? info.element.clone() : info.element,
  			x: x,
  			y: y
  		};
  
  		positions.push(position);
  
  		// Move the element to its final position within the container
  
  		position.element.css({
  			top: Math.round(y),
  			left: Math.round(x),
  			'text-align': halign	// In case the text wraps
  		});
  	};
  
  	// Removes one or more text strings from the canvas text overlay.
  	//
  	// If no parameters are given, all text within the layer is removed.
  	//
  	// Note that the text is not immediately removed; it is simply marked as
  	// inactive, which will result in its removal on the next render pass.
  	// This avoids the performance penalty for 'clear and redraw' behavior,
  	// where we potentially get rid of all text on a layer, but will likely
  	// add back most or all of it later, as when redrawing axes, for example.
  	//
  	// @param {string} layer A string of space-separated CSS classes uniquely
  	//     identifying the layer containing this text.
  	// @param {number=} x X coordinate of the text.
  	// @param {number=} y Y coordinate of the text.
  	// @param {string=} text Text string to remove.
  	// @param {(string|object)=} font Either a string of space-separated CSS
  	//     classes or a font-spec object, defining the text's font and style.
  	// @param {number=} angle Angle at which the text is rotated, in degrees.
  	//     Angle is currently unused, it will be implemented in the future.
  
  	Canvas.prototype.removeText = function(layer, x, y, text, font, angle) {
  		if (text == null) {
  			var layerCache = this._textCache[layer];
  			if (layerCache != null) {
  				for (var styleKey in layerCache) {
  					if (hasOwnProperty.call(layerCache, styleKey)) {
  						var styleCache = layerCache[styleKey];
  						for (var key in styleCache) {
  							if (hasOwnProperty.call(styleCache, key)) {
  								var positions = styleCache[key].positions;
  								for (var i = 0, position; position = positions[i]; i++) {
  									position.active = false;
  								}
  							}
  						}
  					}
  				}
  			}
  		} else {
  			var positions = this.getTextInfo(layer, text, font, angle).positions;
  			for (var i = 0, position; position = positions[i]; i++) {
  				if (position.x == x && position.y == y) {
  					position.active = false;
  				}
  			}
  		}
  	};
  
  	///////////////////////////////////////////////////////////////////////////
  	// The top-level container for the entire plot.
  
      function Plot(placeholder, data_, options_, plugins) {
          // data is on the form:
          //   [ series1, series2 ... ]
          // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
          // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
  
          var series = [],
              options = {
                  // the color theme used for graphs
                  colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
                  legend: {
                      show: true,
                      noColumns: 1, // number of colums in legend table
                      labelFormatter: null, // fn: string -> string
                      labelBoxBorderColor: "#ccc", // border color for the little label boxes
                      container: null, // container (as jQuery object) to put legend in, null means default on top of graph
                      position: "ne", // position of default legend container within plot
                      margin: 5, // distance from grid edge to default legend container within plot
                      backgroundColor: null, // null means auto-detect
                      backgroundOpacity: 0.85, // set to 0 to avoid background
                      sorted: null    // default to no legend sorting
                  },
                  xaxis: {
                      show: null, // null = auto-detect, true = always, false = never
                      position: "bottom", // or "top"
                      mode: null, // null or "time"
                      font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" }
                      color: null, // base color, labels, ticks
                      tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
                      transform: null, // null or f: number -> number to transform axis
                      inverseTransform: null, // if transform is set, this should be the inverse function
                      min: null, // min. value to show, null means set automatically
                      max: null, // max. value to show, null means set automatically
                      autoscaleMargin: null, // margin in % to add if auto-setting min/max
                      ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
                      tickFormatter: null, // fn: number -> string
                      labelWidth: null, // size of tick labels in pixels
                      labelHeight: null,
                      reserveSpace: null, // whether to reserve space even if axis isn't shown
                      tickLength: null, // size in pixels of ticks, or "full" for whole line
                      alignTicksWithAxis: null, // axis number or null for no sync
                      tickDecimals: null, // no. of decimals, null means auto
                      tickSize: null, // number or [number, "unit"]
                      minTickSize: null // number or [number, "unit"]
                  },
                  yaxis: {
                      autoscaleMargin: 0.02,
                      position: "left" // or "right"
                  },
                  xaxes: [],
                  yaxes: [],
                  series: {
                      points: {
                          show: false,
                          radius: 3,
                          lineWidth: 2, // in pixels
                          fill: true,
                          fillColor: "#ffffff",
                          symbol: "circle" // or callback
                      },
                      lines: {
                          // we don't put in show: false so we can see
                          // whether lines were actively disabled
                          lineWidth: 2, // in pixels
                          fill: false,
                          fillColor: null,
                          steps: false
                          // Omit 'zero', so we can later default its value to
                          // match that of the 'fill' option.
                      },
                      bars: {
                          show: false,
                          lineWidth: 2, // in pixels
                          barWidth: 1, // in units of the x axis
                          fill: true,
                          fillColor: null,
                          align: "left", // "left", "right", or "center"
                          horizontal: false,
                          zero: true
                      },
                      shadowSize: 3,
                      highlightColor: null
                  },
                  grid: {
                      show: true,
                      aboveData: false,
                      color: "#545454", // primary color used for outline and labels
                      backgroundColor: null, // null for transparent, else color
                      borderColor: null, // set if different from the grid color
                      tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
                      margin: 0, // distance from the canvas edge to the grid
                      labelMargin: 5, // in pixels
                      axisMargin: 8, // in pixels
                      borderWidth: 2, // in pixels
                      minBorderMargin: null, // in pixels, null means taken from points radius
                      markings: null, // array of ranges or fn: axes -> array of ranges
                      markingsColor: "#f4f4f4",
                      markingsLineWidth: 2,
                      // interactive stuff
                      clickable: false,
                      hoverable: false,
                      autoHighlight: true, // highlight in case mouse is near
                      mouseActiveRadius: 10 // how far the mouse can be away to activate an item
                  },
                  interaction: {
                      redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow
                  },
                  hooks: {}
              },
          surface = null,     // the canvas for the plot itself
          overlay = null,     // canvas for interactive stuff on top of plot
          eventHolder = null, // jQuery object that events should be bound to
          ctx = null, octx = null,
          xaxes = [], yaxes = [],
          plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
          plotWidth = 0, plotHeight = 0,
          hooks = {
              processOptions: [],
              processRawData: [],
              processDatapoints: [],
              processOffset: [],
              drawBackground: [],
              drawSeries: [],
              draw: [],
              bindEvents: [],
              drawOverlay: [],
              shutdown: []
          },
          plot = this;
  
          // public functions
          plot.setData = setData;
          plot.setupGrid = setupGrid;
          plot.draw = draw;
          plot.getPlaceholder = function() { return placeholder; };
          plot.getCanvas = function() { return surface.element; };
          plot.getPlotOffset = function() { return plotOffset; };
          plot.width = function () { return plotWidth; };
          plot.height = function () { return plotHeight; };
          plot.offset = function () {
              var o = eventHolder.offset();
              o.left += plotOffset.left;
              o.top += plotOffset.top;
              return o;
          };
          plot.getData = function () { return series; };
          plot.getAxes = function () {
              var res = {}, i;
              $.each(xaxes.concat(yaxes), function (_, axis) {
                  if (axis)
                      res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
              });
              return res;
          };
          plot.getXAxes = function () { return xaxes; };
          plot.getYAxes = function () { return yaxes; };
          plot.c2p = canvasToAxisCoords;
          plot.p2c = axisToCanvasCoords;
          plot.getOptions = function () { return options; };
          plot.highlight = highlight;
          plot.unhighlight = unhighlight;
          plot.triggerRedrawOverlay = triggerRedrawOverlay;
          plot.pointOffset = function(point) {
              return {
                  left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10),
                  top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10)
              };
          };
          plot.shutdown = shutdown;
          plot.destroy = function () {
              shutdown();
              placeholder.removeData("plot").empty();
  
              series = [];
              options = null;
              surface = null;
              overlay = null;
              eventHolder = null;
              ctx = null;
              octx = null;
              xaxes = [];
              yaxes = [];
              hooks = null;
              highlights = [];
              plot = null;
          };
          plot.resize = function () {
          	var width = placeholder.width(),
          		height = placeholder.height();
              surface.resize(width, height);
              overlay.resize(width, height);
          };
  
          // public attributes
          plot.hooks = hooks;
  
          // initialize
          initPlugins(plot);
          parseOptions(options_);
          setupCanvases();
          setData(data_);
          setupGrid();
          draw();
          bindEvents();
  
  
          function executeHooks(hook, args) {
              args = [plot].concat(args);
              for (var i = 0; i < hook.length; ++i)
                  hook[i].apply(this, args);
          }
  
          function initPlugins() {
  
              // References to key classes, allowing plugins to modify them
  
              var classes = {
                  Canvas: Canvas
              };
  
              for (var i = 0; i < plugins.length; ++i) {
                  var p = plugins[i];
                  p.init(plot, classes);
                  if (p.options)
                      $.extend(true, options, p.options);
              }
          }
  
          function parseOptions(opts) {
  
              $.extend(true, options, opts);
  
              // $.extend merges arrays, rather than replacing them.  When less
              // colors are provided than the size of the default palette, we
              // end up with those colors plus the remaining defaults, which is
              // not expected behavior; avoid it by replacing them here.
  
              if (opts && opts.colors) {
              	options.colors = opts.colors;
              }
  
              if (options.xaxis.color == null)
                  options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
              if (options.yaxis.color == null)
                  options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
  
              if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility
                  options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color;
              if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility
                  options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color;
  
              if (options.grid.borderColor == null)
                  options.grid.borderColor = options.grid.color;
              if (options.grid.tickColor == null)
                  options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
  
              // Fill in defaults for axis options, including any unspecified
              // font-spec fields, if a font-spec was provided.
  
              // If no x/y axis options were provided, create one of each anyway,
              // since the rest of the code assumes that they exist.
  
              var i, axisOptions, axisCount,
                  fontSize = placeholder.css("font-size"),
                  fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13,
                  fontDefaults = {
                      style: placeholder.css("font-style"),
                      size: Math.round(0.8 * fontSizeDefault),
                      variant: placeholder.css("font-variant"),
                      weight: placeholder.css("font-weight"),
                      family: placeholder.css("font-family")
                  };
  
              axisCount = options.xaxes.length || 1;
              for (i = 0; i < axisCount; ++i) {
  
                  axisOptions = options.xaxes[i];
                  if (axisOptions && !axisOptions.tickColor) {
                      axisOptions.tickColor = axisOptions.color;
                  }
  
                  axisOptions = $.extend(true, {}, options.xaxis, axisOptions);
                  options.xaxes[i] = axisOptions;
  
                  if (axisOptions.font) {
                      axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
                      if (!axisOptions.font.color) {
                          axisOptions.font.color = axisOptions.color;
                      }
                      if (!axisOptions.font.lineHeight) {
                          axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
                      }
                  }
              }
  
              axisCount = options.yaxes.length || 1;
              for (i = 0; i < axisCount; ++i) {
  
                  axisOptions = options.yaxes[i];
                  if (axisOptions && !axisOptions.tickColor) {
                      axisOptions.tickColor = axisOptions.color;
                  }
  
                  axisOptions = $.extend(true, {}, options.yaxis, axisOptions);
                  options.yaxes[i] = axisOptions;
  
                  if (axisOptions.font) {
                      axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
                      if (!axisOptions.font.color) {
                          axisOptions.font.color = axisOptions.color;
                      }
                      if (!axisOptions.font.lineHeight) {
                          axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
                      }
                  }
              }
  
              // backwards compatibility, to be removed in future
              if (options.xaxis.noTicks && options.xaxis.ticks == null)
                  options.xaxis.ticks = options.xaxis.noTicks;
              if (options.yaxis.noTicks && options.yaxis.ticks == null)
                  options.yaxis.ticks = options.yaxis.noTicks;
              if (options.x2axis) {
                  options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
                  options.xaxes[1].position = "top";
                  // Override the inherit to allow the axis to auto-scale
                  if (options.x2axis.min == null) {
                      options.xaxes[1].min = null;
                  }
                  if (options.x2axis.max == null) {
                      options.xaxes[1].max = null;
                  }
              }
              if (options.y2axis) {
                  options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
                  options.yaxes[1].position = "right";
                  // Override the inherit to allow the axis to auto-scale
                  if (options.y2axis.min == null) {
                      options.yaxes[1].min = null;
                  }
                  if (options.y2axis.max == null) {
                      options.yaxes[1].max = null;
                  }
              }
              if (options.grid.coloredAreas)
                  options.grid.markings = options.grid.coloredAreas;
              if (options.grid.coloredAreasColor)
                  options.grid.markingsColor = options.grid.coloredAreasColor;
              if (options.lines)
                  $.extend(true, options.series.lines, options.lines);
              if (options.points)
                  $.extend(true, options.series.points, options.points);
              if (options.bars)
                  $.extend(true, options.series.bars, options.bars);
              if (options.shadowSize != null)
                  options.series.shadowSize = options.shadowSize;
              if (options.highlightColor != null)
                  options.series.highlightColor = options.highlightColor;
  
              // save options on axes for future reference
              for (i = 0; i < options.xaxes.length; ++i)
                  getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
              for (i = 0; i < options.yaxes.length; ++i)
                  getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
  
              // add hooks from options
              for (var n in hooks)
                  if (options.hooks[n] && options.hooks[n].length)
                      hooks[n] = hooks[n].concat(options.hooks[n]);
  
              executeHooks(hooks.processOptions, [options]);
          }
  
          function setData(d) {
              series = parseData(d);
              fillInSeriesOptions();
              processData();
          }
  
          function parseData(d) {
              var res = [];
              for (var i = 0; i < d.length; ++i) {
                  var s = $.extend(true, {}, options.series);
  
                  if (d[i].data != null) {
                      s.data = d[i].data; // move the data instead of deep-copy
                      delete d[i].data;
  
                      $.extend(true, s, d[i]);
  
                      d[i].data = s.data;
                  }
                  else
                      s.data = d[i];
                  res.push(s);
              }
  
              return res;
          }
  
          function axisNumber(obj, coord) {
              var a = obj[coord + "axis"];
              if (typeof a == "object") // if we got a real axis, extract number
                  a = a.n;
              if (typeof a != "number")
                  a = 1; // default to first axis
              return a;
          }
  
          function allAxes() {
              // return flat array without annoying null entries
              return $.grep(xaxes.concat(yaxes), function (a) { return a; });
          }
  
          function canvasToAxisCoords(pos) {
              // return an object with x/y corresponding to all used axes
              var res = {}, i, axis;
              for (i = 0; i < xaxes.length; ++i) {
                  axis = xaxes[i];
                  if (axis && axis.used)
                      res["x" + axis.n] = axis.c2p(pos.left);
              }
  
              for (i = 0; i < yaxes.length; ++i) {
                  axis = yaxes[i];
                  if (axis && axis.used)
                      res["y" + axis.n] = axis.c2p(pos.top);
              }
  
              if (res.x1 !== undefined)
                  res.x = res.x1;
              if (res.y1 !== undefined)
                  res.y = res.y1;
  
              return res;
          }
  
          function axisToCanvasCoords(pos) {
              // get canvas coords from the first pair of x/y found in pos
              var res = {}, i, axis, key;
  
              for (i = 0; i < xaxes.length; ++i) {
                  axis = xaxes[i];
                  if (axis && axis.used) {
                      key = "x" + axis.n;
                      if (pos[key] == null && axis.n == 1)
                          key = "x";
  
                      if (pos[key] != null) {
                          res.left = axis.p2c(pos[key]);
                          break;
                      }
                  }
              }
  
              for (i = 0; i < yaxes.length; ++i) {
                  axis = yaxes[i];
                  if (axis && axis.used) {
                      key = "y" + axis.n;
                      if (pos[key] == null && axis.n == 1)
                          key = "y";
  
                      if (pos[key] != null) {
                          res.top = axis.p2c(pos[key]);
                          break;
                      }
                  }
              }
  
              return res;
          }
  
          function getOrCreateAxis(axes, number) {
              if (!axes[number - 1])
                  axes[number - 1] = {
                      n: number, // save the number for future reference
                      direction: axes == xaxes ? "x" : "y",
                      options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
                  };
  
              return axes[number - 1];
          }
  
          function fillInSeriesOptions() {
  
              var neededColors = series.length, maxIndex = -1, i;
  
              // Subtract the number of series that already have fixed colors or
              // color indexes from the number that we still need to generate.
  
              for (i = 0; i < series.length; ++i) {
                  var sc = series[i].color;
                  if (sc != null) {
                      neededColors--;
                      if (typeof sc == "number" && sc > maxIndex) {
                          maxIndex = sc;
                      }
                  }
              }
  
              // If any of the series have fixed color indexes, then we need to
              // generate at least as many colors as the highest index.
  
              if (neededColors <= maxIndex) {
                  neededColors = maxIndex + 1;
              }
  
              // Generate all the colors, using first the option colors and then
              // variations on those colors once they're exhausted.
  
              var c, colors = [], colorPool = options.colors,
                  colorPoolSize = colorPool.length, variation = 0;
  
              for (i = 0; i < neededColors; i++) {
  
                  c = $.color.parse(colorPool[i % colorPoolSize] || "#666");
  
                  // Each time we exhaust the colors in the pool we adjust
                  // a scaling factor used to produce more variations on
                  // those colors. The factor alternates negative/positive
                  // to produce lighter/darker colors.
  
                  // Reset the variation after every few cycles, or else
                  // it will end up producing only white or black colors.
  
                  if (i % colorPoolSize == 0 && i) {
                      if (variation >= 0) {
                          if (variation < 0.5) {
                              variation = -variation - 0.2;
                          } else variation = 0;
                      } else variation = -variation;
                  }
  
                  colors[i] = c.scale('rgb', 1 + variation);
              }
  
              // Finalize the series options, filling in their colors
  
              var colori = 0, s;
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
  
                  // assign colors
                  if (s.color == null) {
                      s.color = colors[colori].toString();
                      ++colori;
                  }
                  else if (typeof s.color == "number")
                      s.color = colors[s.color].toString();
  
                  // turn on lines automatically in case nothing is set
                  if (s.lines.show == null) {
                      var v, show = true;
                      for (v in s)
                          if (s[v] && s[v].show) {
                              show = false;
                              break;
                          }
                      if (show)
                          s.lines.show = true;
                  }
  
                  // If nothing was provided for lines.zero, default it to match
                  // lines.fill, since areas by default should extend to zero.
  
                  if (s.lines.zero == null) {
                      s.lines.zero = !!s.lines.fill;
                  }
  
                  // setup axes
                  s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
                  s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
              }
          }
  
          function processData() {
              var topSentry = Number.POSITIVE_INFINITY,
                  bottomSentry = Number.NEGATIVE_INFINITY,
                  fakeInfinity = Number.MAX_VALUE,
                  i, j, k, m, length,
                  s, points, ps, x, y, axis, val, f, p,
                  data, format;
  
              function updateAxis(axis, min, max) {
                  if (min < axis.datamin && min != -fakeInfinity)
                      axis.datamin = min;
                  if (max > axis.datamax && max != fakeInfinity)
                      axis.datamax = max;
              }
  
              $.each(allAxes(), function (_, axis) {
                  // init axis
                  axis.datamin = topSentry;
                  axis.datamax = bottomSentry;
                  axis.used = false;
              });
  
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
                  s.datapoints = { points: [] };
  
                  executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
              }
  
              // first pass: clean and copy data
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
  
                  data = s.data;
                  format = s.datapoints.format;
  
                  if (!format) {
                      format = [];
                      // find out how to copy
                      format.push({ x: true, number: true, required: true });
                      format.push({ y: true, number: true, required: true });
  
                      if (s.bars.show || (s.lines.show && s.lines.fill)) {
                          var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
                          format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale });
                          if (s.bars.horizontal) {
                              delete format[format.length - 1].y;
                              format[format.length - 1].x = true;
                          }
                      }
  
                      s.datapoints.format = format;
                  }
  
                  if (s.datapoints.pointsize != null)
                      continue; // already filled in
  
                  s.datapoints.pointsize = format.length;
  
                  ps = s.datapoints.pointsize;
                  points = s.datapoints.points;
  
                  var insertSteps = s.lines.show && s.lines.steps;
                  s.xaxis.used = s.yaxis.used = true;
  
                  for (j = k = 0; j < data.length; ++j, k += ps) {
                      p = data[j];
  
                      var nullify = p == null;
                      if (!nullify) {
                          for (m = 0; m < ps; ++m) {
                              val = p[m];
                              f = format[m];
  
                              if (f) {
                                  if (f.number && val != null) {
                                      val = +val; // convert to number
                                      if (isNaN(val))
                                          val = null;
                                      else if (val == Infinity)
                                          val = fakeInfinity;
                                      else if (val == -Infinity)
                                          val = -fakeInfinity;
                                  }
  
                                  if (val == null) {
                                      if (f.required)
                                          nullify = true;
  
                                      if (f.defaultValue != null)
                                          val = f.defaultValue;
                                  }
                              }
  
                              points[k + m] = val;
                          }
                      }
  
                      if (nullify) {
                          for (m = 0; m < ps; ++m) {
                              val = points[k + m];
                              if (val != null) {
                                  f = format[m];
                                  // extract min/max info
                                  if (f.autoscale !== false) {
                                      if (f.x) {
                                          updateAxis(s.xaxis, val, val);
                                      }
                                      if (f.y) {
                                          updateAxis(s.yaxis, val, val);
                                      }
                                  }
                              }
                              points[k + m] = null;
                          }
                      }
                      else {
                          // a little bit of line specific stuff that
                          // perhaps shouldn't be here, but lacking
                          // better means...
                          if (insertSteps && k > 0
                              && points[k - ps] != null
                              && points[k - ps] != points[k]
                              && points[k - ps + 1] != points[k + 1]) {
                              // copy the point to make room for a middle point
                              for (m = 0; m < ps; ++m)
                                  points[k + ps + m] = points[k + m];
  
                              // middle point has same y
                              points[k + 1] = points[k - ps + 1];
  
                              // we've added a point, better reflect that
                              k += ps;
                          }
                      }
                  }
              }
  
              // give the hooks a chance to run
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
  
                  executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
              }
  
              // second pass: find datamax/datamin for auto-scaling
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
                  points = s.datapoints.points;
                  ps = s.datapoints.pointsize;
                  format = s.datapoints.format;
  
                  var xmin = topSentry, ymin = topSentry,
                      xmax = bottomSentry, ymax = bottomSentry;
  
                  for (j = 0; j < points.length; j += ps) {
                      if (points[j] == null)
                          continue;
  
                      for (m = 0; m < ps; ++m) {
                          val = points[j + m];
                          f = format[m];
                          if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity)
                              continue;
  
                          if (f.x) {
                              if (val < xmin)
                                  xmin = val;
                              if (val > xmax)
                                  xmax = val;
                          }
                          if (f.y) {
                              if (val < ymin)
                                  ymin = val;
                              if (val > ymax)
                                  ymax = val;
                          }
                      }
                  }
  
                  if (s.bars.show) {
                      // make sure we got room for the bar on the dancing floor
                      var delta;
  
                      switch (s.bars.align) {
                          case "left":
                              delta = 0;
                              break;
                          case "right":
                              delta = -s.bars.barWidth;
                              break;
                          default:
                              delta = -s.bars.barWidth / 2;
                      }
  
                      if (s.bars.horizontal) {
                          ymin += delta;
                          ymax += delta + s.bars.barWidth;
                      }
                      else {
                          xmin += delta;
                          xmax += delta + s.bars.barWidth;
                      }
                  }
  
                  updateAxis(s.xaxis, xmin, xmax);
                  updateAxis(s.yaxis, ymin, ymax);
              }
  
              $.each(allAxes(), function (_, axis) {
                  if (axis.datamin == topSentry)
                      axis.datamin = null;
                  if (axis.datamax == bottomSentry)
                      axis.datamax = null;
              });
          }
  
          function setupCanvases() {
  
              // Make sure the placeholder is clear of everything except canvases
              // from a previous plot in this container that we'll try to re-use.
  
              placeholder.css("padding", 0) // padding messes up the positioning
                  .children().filter(function(){
                      return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base');
                  }).remove();
  
              if (placeholder.css("position") == 'static')
                  placeholder.css("position", "relative"); // for positioning labels and overlay
  
              surface = new Canvas("flot-base", placeholder);
              overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features
  
              ctx = surface.context;
              octx = overlay.context;
  
              // define which element we're listening for events on
              eventHolder = $(overlay.element).unbind();
  
              // If we're re-using a plot object, shut down the old one
  
              var existing = placeholder.data("plot");
  
              if (existing) {
                  existing.shutdown();
                  overlay.clear();
              }
  
              // save in case we get replotted
              placeholder.data("plot", plot);
          }
  
          function bindEvents() {
              // bind events
              if (options.grid.hoverable) {
                  eventHolder.mousemove(onMouseMove);
  
                  // Use bind, rather than .mouseleave, because we officially
                  // still support jQuery 1.2.6, which doesn't define a shortcut
                  // for mouseenter or mouseleave.  This was a bug/oversight that
                  // was fixed somewhere around 1.3.x.  We can return to using
                  // .mouseleave when we drop support for 1.2.6.
  
                  eventHolder.bind("mouseleave", onMouseLeave);
              }
  
              if (options.grid.clickable)
                  eventHolder.click(onClick);
  
              executeHooks(hooks.bindEvents, [eventHolder]);
          }
  
          function shutdown() {
              if (redrawTimeout)
                  clearTimeout(redrawTimeout);
  
              eventHolder.unbind("mousemove", onMouseMove);
              eventHolder.unbind("mouseleave", onMouseLeave);
              eventHolder.unbind("click", onClick);
  
              executeHooks(hooks.shutdown, [eventHolder]);
          }
  
          function setTransformationHelpers(axis) {
              // set helper functions on the axis, assumes plot area
              // has been computed already
  
              function identity(x) { return x; }
  
              var s, m, t = axis.options.transform || identity,
                  it = axis.options.inverseTransform;
  
              // precompute how much the axis is scaling a point
              // in canvas space
              if (axis.direction == "x") {
                  s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
                  m = Math.min(t(axis.max), t(axis.min));
              }
              else {
                  s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
                  s = -s;
                  m = Math.max(t(axis.max), t(axis.min));
              }
  
              // data point to canvas coordinate
              if (t == identity) // slight optimization
                  axis.p2c = function (p) { return (p - m) * s; };
              else
                  axis.p2c = function (p) { return (t(p) - m) * s; };
              // canvas coordinate to data point
              if (!it)
                  axis.c2p = function (c) { return m + c / s; };
              else
                  axis.c2p = function (c) { return it(m + c / s); };
          }
  
          function measureTickLabels(axis) {
  
              var opts = axis.options,
                  ticks = axis.ticks || [],
                  labelWidth = opts.labelWidth || 0,
                  labelHeight = opts.labelHeight || 0,
                  maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null),
                  legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
                  layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
                  font = opts.font || "flot-tick-label tickLabel";
  
              for (var i = 0; i < ticks.length; ++i) {
  
                  var t = ticks[i];
  
                  if (!t.label)
                      continue;
  
                  var info = surface.getTextInfo(layer, t.label, font, null, maxWidth);
  
                  labelWidth = Math.max(labelWidth, info.width);
                  labelHeight = Math.max(labelHeight, info.height);
              }
  
              axis.labelWidth = opts.labelWidth || labelWidth;
              axis.labelHeight = opts.labelHeight || labelHeight;
          }
  
          function allocateAxisBoxFirstPhase(axis) {
              // find the bounding box of the axis by looking at label
              // widths/heights and ticks, make room by diminishing the
              // plotOffset; this first phase only looks at one
              // dimension per axis, the other dimension depends on the
              // other axes so will have to wait
  
              var lw = axis.labelWidth,
                  lh = axis.labelHeight,
                  pos = axis.options.position,
                  isXAxis = axis.direction === "x",
                  tickLength = axis.options.tickLength,
                  axisMargin = options.grid.axisMargin,
                  padding = options.grid.labelMargin,
                  innermost = true,
                  outermost = true,
                  first = true,
                  found = false;
  
              // Determine the axis's position in its direction and on its side
  
              $.each(isXAxis ? xaxes : yaxes, function(i, a) {
                  if (a && (a.show || a.reserveSpace)) {
                      if (a === axis) {
                          found = true;
                      } else if (a.options.position === pos) {
                          if (found) {
                              outermost = false;
                          } else {
                              innermost = false;
                          }
                      }
                      if (!found) {
                          first = false;
                      }
                  }
              });
  
              // The outermost axis on each side has no margin
  
              if (outermost) {
                  axisMargin = 0;
              }
  
              // The ticks for the first axis in each direction stretch across
  
              if (tickLength == null) {
                  tickLength = first ? "full" : 5;
              }
  
              if (!isNaN(+tickLength))
                  padding += +tickLength;
  
              if (isXAxis) {
                  lh += padding;
  
                  if (pos == "bottom") {
                      plotOffset.bottom += lh + axisMargin;
                      axis.box = { top: surface.height - plotOffset.bottom, height: lh };
                  }
                  else {
                      axis.box = { top: plotOffset.top + axisMargin, height: lh };
                      plotOffset.top += lh + axisMargin;
                  }
              }
              else {
                  lw += padding;
  
                  if (pos == "left") {
                      axis.box = { left: plotOffset.left + axisMargin, width: lw };
                      plotOffset.left += lw + axisMargin;
                  }
                  else {
                      plotOffset.right += lw + axisMargin;
                      axis.box = { left: surface.width - plotOffset.right, width: lw };
                  }
              }
  
               // save for future reference
              axis.position = pos;
              axis.tickLength = tickLength;
              axis.box.padding = padding;
              axis.innermost = innermost;
          }
  
          function allocateAxisBoxSecondPhase(axis) {
              // now that all axis boxes have been placed in one
              // dimension, we can set the remaining dimension coordinates
              if (axis.direction == "x") {
                  axis.box.left = plotOffset.left - axis.labelWidth / 2;
                  axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth;
              }
              else {
                  axis.box.top = plotOffset.top - axis.labelHeight / 2;
                  axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight;
              }
          }
  
          function adjustLayoutForThingsStickingOut() {
              // possibly adjust plot offset to ensure everything stays
              // inside the canvas and isn't clipped off
  
              var minMargin = options.grid.minBorderMargin,
                  axis, i;
  
              // check stuff from the plot (FIXME: this should just read
              // a value from the series, otherwise it's impossible to
              // customize)
              if (minMargin == null) {
                  minMargin = 0;
                  for (i = 0; i < series.length; ++i)
                      minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
              }
  
              var margins = {
                  left: minMargin,
                  right: minMargin,
                  top: minMargin,
                  bottom: minMargin
              };
  
              // check axis labels, note we don't check the actual
              // labels but instead use the overall width/height to not
              // jump as much around with replots
              $.each(allAxes(), function (_, axis) {
                  if (axis.reserveSpace && axis.ticks && axis.ticks.length) {
                      if (axis.direction === "x") {
                          margins.left = Math.max(margins.left, axis.labelWidth / 2);
                          margins.right = Math.max(margins.right, axis.labelWidth / 2);
                      } else {
                          margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2);
                          margins.top = Math.max(margins.top, axis.labelHeight / 2);
                      }
                  }
              });
  
              plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left));
              plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right));
              plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top));
              plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom));
          }
  
          function setupGrid() {
              var i, axes = allAxes(), showGrid = options.grid.show;
  
              // Initialize the plot's offset from the edge of the canvas
  
              for (var a in plotOffset) {
                  var margin = options.grid.margin || 0;
                  plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0;
              }
  
              executeHooks(hooks.processOffset, [plotOffset]);
  
              // If the grid is visible, add its border width to the offset
  
              for (var a in plotOffset) {
                  if(typeof(options.grid.borderWidth) == "object") {
                      plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0;
                  }
                  else {
                      plotOffset[a] += showGrid ? options.grid.borderWidth : 0;
                  }
              }
  
              $.each(axes, function (_, axis) {
                  var axisOpts = axis.options;
                  axis.show = axisOpts.show == null ? axis.used : axisOpts.show;
                  axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace;
                  setRange(axis);
              });
  
              if (showGrid) {
  
                  var allocatedAxes = $.grep(axes, function (axis) {
                      return axis.show || axis.reserveSpace;
                  });
  
                  $.each(allocatedAxes, function (_, axis) {
                      // make the ticks
                      setupTickGeneration(axis);
                      setTicks(axis);
                      snapRangeToTicks(axis, axis.ticks);
                      // find labelWidth/Height for axis
                      measureTickLabels(axis);
                  });
  
                  // with all dimensions calculated, we can compute the
                  // axis bounding boxes, start from the outside
                  // (reverse order)
                  for (i = allocatedAxes.length - 1; i >= 0; --i)
                      allocateAxisBoxFirstPhase(allocatedAxes[i]);
  
                  // make sure we've got enough space for things that
                  // might stick out
                  adjustLayoutForThingsStickingOut();
  
                  $.each(allocatedAxes, function (_, axis) {
                      allocateAxisBoxSecondPhase(axis);
                  });
              }
  
              plotWidth = surface.width - plotOffset.left - plotOffset.right;
              plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
  
              // now we got the proper plot dimensions, we can compute the scaling
              $.each(axes, function (_, axis) {
                  setTransformationHelpers(axis);
              });
  
              if (showGrid) {
                  drawAxisLabels();
              }
  
              insertLegend();
          }
  
          function setRange(axis) {
              var opts = axis.options,
                  min = +(opts.min != null ? opts.min : axis.datamin),
                  max = +(opts.max != null ? opts.max : axis.datamax),
                  delta = max - min;
  
              if (delta == 0.0) {
                  // degenerate case
                  var widen = max == 0 ? 1 : 0.01;
  
                  if (opts.min == null)
                      min -= widen;
                  // always widen max if we couldn't widen min to ensure we
                  // don't fall into min == max which doesn't work
                  if (opts.max == null || opts.min != null)
                      max += widen;
              }
              else {
                  // consider autoscaling
                  var margin = opts.autoscaleMargin;
                  if (margin != null) {
                      if (opts.min == null) {
                          min -= delta * margin;
                          // make sure we don't go below zero if all values
                          // are positive
                          if (min < 0 && axis.datamin != null && axis.datamin >= 0)
                              min = 0;
                      }
                      if (opts.max == null) {
                          max += delta * margin;
                          if (max > 0 && axis.datamax != null && axis.datamax <= 0)
                              max = 0;
                      }
                  }
              }
              axis.min = min;
              axis.max = max;
          }
  
          function setupTickGeneration(axis) {
              var opts = axis.options;
  
              // estimate number of ticks
              var noTicks;
              if (typeof opts.ticks == "number" && opts.ticks > 0)
                  noTicks = opts.ticks;
              else
                  // heuristic based on the model a*sqrt(x) fitted to
                  // some data points that seemed reasonable
                  noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height);
  
              var delta = (axis.max - axis.min) / noTicks,
                  dec = -Math.floor(Math.log(delta) / Math.LN10),
                  maxDec = opts.tickDecimals;
  
              if (maxDec != null && dec > maxDec) {
                  dec = maxDec;
              }
  
              var magn = Math.pow(10, -dec),
                  norm = delta / magn, // norm is between 1.0 and 10.0
                  size;
  
              if (norm < 1.5) {
                  size = 1;
              } else if (norm < 3) {
                  size = 2;
                  // special case for 2.5, requires an extra decimal
                  if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
                      size = 2.5;
                      ++dec;
                  }
              } else if (norm < 7.5) {
                  size = 5;
              } else {
                  size = 10;
              }
  
              size *= magn;
  
              if (opts.minTickSize != null && size < opts.minTickSize) {
                  size = opts.minTickSize;
              }
  
              axis.delta = delta;
              axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
              axis.tickSize = opts.tickSize || size;
  
              // Time mode was moved to a plug-in in 0.8, and since so many people use it
              // we'll add an especially friendly reminder to make sure they included it.
  
              if (opts.mode == "time" && !axis.tickGenerator) {
                  throw new Error("Time mode requires the flot.time plugin.");
              }
  
              // Flot supports base-10 axes; any other mode else is handled by a plug-in,
              // like flot.time.js.
  
              if (!axis.tickGenerator) {
  
                  axis.tickGenerator = function (axis) {
  
                      var ticks = [],
                          start = floorInBase(axis.min, axis.tickSize),
                          i = 0,
                          v = Number.NaN,
                          prev;
  
                      do {
                          prev = v;
                          v = start + i * axis.tickSize;
                          ticks.push(v);
                          ++i;
                      } while (v < axis.max && v != prev);
                      return ticks;
                  };
  
  				axis.tickFormatter = function (value, axis) {
  
  					var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1;
  					var formatted = "" + Math.round(value * factor) / factor;
  
  					// If tickDecimals was specified, ensure that we have exactly that
  					// much precision; otherwise default to the value's own precision.
  
  					if (axis.tickDecimals != null) {
  						var decimal = formatted.indexOf(".");
  						var precision = decimal == -1 ? 0 : formatted.length - decimal - 1;
  						if (precision < axis.tickDecimals) {
  							return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision);
  						}
  					}
  
                      return formatted;
                  };
              }
  
              if ($.isFunction(opts.tickFormatter))
                  axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
  
              if (opts.alignTicksWithAxis != null) {
                  var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
                  if (otherAxis && otherAxis.used && otherAxis != axis) {
                      // consider snapping min/max to outermost nice ticks
                      var niceTicks = axis.tickGenerator(axis);
                      if (niceTicks.length > 0) {
                          if (opts.min == null)
                              axis.min = Math.min(axis.min, niceTicks[0]);
                          if (opts.max == null && niceTicks.length > 1)
                              axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
                      }
  
                      axis.tickGenerator = function (axis) {
                          // copy ticks, scaled to this axis
                          var ticks = [], v, i;
                          for (i = 0; i < otherAxis.ticks.length; ++i) {
                              v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
                              v = axis.min + v * (axis.max - axis.min);
                              ticks.push(v);
                          }
                          return ticks;
                      };
  
                      // we might need an extra decimal since forced
                      // ticks don't necessarily fit naturally
                      if (!axis.mode && opts.tickDecimals == null) {
                          var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1),
                              ts = axis.tickGenerator(axis);
  
                          // only proceed if the tick interval rounded
                          // with an extra decimal doesn't give us a
                          // zero at end
                          if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
                              axis.tickDecimals = extraDec;
                      }
                  }
              }
          }
  
          function setTicks(axis) {
              var oticks = axis.options.ticks, ticks = [];
              if (oticks == null || (typeof oticks == "number" && oticks > 0))
                  ticks = axis.tickGenerator(axis);
              else if (oticks) {
                  if ($.isFunction(oticks))
                      // generate the ticks
                      ticks = oticks(axis);
                  else
                      ticks = oticks;
              }
  
              // clean up/labelify the supplied ticks, copy them over
              var i, v;
              axis.ticks = [];
              for (i = 0; i < ticks.length; ++i) {
                  var label = null;
                  var t = ticks[i];
                  if (typeof t == "object") {
                      v = +t[0];
                      if (t.length > 1)
                          label = t[1];
                  }
                  else
                      v = +t;
                  if (label == null)
                      label = axis.tickFormatter(v, axis);
                  if (!isNaN(v))
                      axis.ticks.push({ v: v, label: label });
              }
          }
  
          function snapRangeToTicks(axis, ticks) {
              if (axis.options.autoscaleMargin && ticks.length > 0) {
                  // snap to ticks
                  if (axis.options.min == null)
                      axis.min = Math.min(axis.min, ticks[0].v);
                  if (axis.options.max == null && ticks.length > 1)
                      axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
              }
          }
  
          function draw() {
  
              surface.clear();
  
              executeHooks(hooks.drawBackground, [ctx]);
  
              var grid = options.grid;
  
              // draw background, if any
              if (grid.show && grid.backgroundColor)
                  drawBackground();
  
              if (grid.show && !grid.aboveData) {
                  drawGrid();
              }
  
              for (var i = 0; i < series.length; ++i) {
                  executeHooks(hooks.drawSeries, [ctx, series[i]]);
                  drawSeries(series[i]);
              }
  
              executeHooks(hooks.draw, [ctx]);
  
              if (grid.show && grid.aboveData) {
                  drawGrid();
              }
  
              surface.render();
  
              // A draw implies that either the axes or data have changed, so we
              // should probably update the overlay highlights as well.
  
              triggerRedrawOverlay();
          }
  
          function extractRange(ranges, coord) {
              var axis, from, to, key, axes = allAxes();
  
              for (var i = 0; i < axes.length; ++i) {
                  axis = axes[i];
                  if (axis.direction == coord) {
                      key = coord + axis.n + "axis";
                      if (!ranges[key] && axis.n == 1)
                          key = coord + "axis"; // support x1axis as xaxis
                      if (ranges[key]) {
                          from = ranges[key].from;
                          to = ranges[key].to;
                          break;
                      }
                  }
              }
  
              // backwards-compat stuff - to be removed in future
              if (!ranges[key]) {
                  axis = coord == "x" ? xaxes[0] : yaxes[0];
                  from = ranges[coord + "1"];
                  to = ranges[coord + "2"];
              }
  
              // auto-reverse as an added bonus
              if (from != null && to != null && from > to) {
                  var tmp = from;
                  from = to;
                  to = tmp;
              }
  
              return { from: from, to: to, axis: axis };
          }
  
          function drawBackground() {
              ctx.save();
              ctx.translate(plotOffset.left, plotOffset.top);
  
              ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
              ctx.fillRect(0, 0, plotWidth, plotHeight);
              ctx.restore();
          }
  
          function drawGrid() {
              var i, axes, bw, bc;
  
              ctx.save();
              ctx.translate(plotOffset.left, plotOffset.top);
  
              // draw markings
              var markings = options.grid.markings;
              if (markings) {
                  if ($.isFunction(markings)) {
                      axes = plot.getAxes();
                      // xmin etc. is backwards compatibility, to be
                      // removed in the future
                      axes.xmin = axes.xaxis.min;
                      axes.xmax = axes.xaxis.max;
                      axes.ymin = axes.yaxis.min;
                      axes.ymax = axes.yaxis.max;
  
                      markings = markings(axes);
                  }
  
                  for (i = 0; i < markings.length; ++i) {
                      var m = markings[i],
                          xrange = extractRange(m, "x"),
                          yrange = extractRange(m, "y");
  
                      // fill in missing
                      if (xrange.from == null)
                          xrange.from = xrange.axis.min;
                      if (xrange.to == null)
                          xrange.to = xrange.axis.max;
                      if (yrange.from == null)
                          yrange.from = yrange.axis.min;
                      if (yrange.to == null)
                          yrange.to = yrange.axis.max;
  
                      // clip
                      if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
                          yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
                          continue;
  
                      xrange.from = Math.max(xrange.from, xrange.axis.min);
                      xrange.to = Math.min(xrange.to, xrange.axis.max);
                      yrange.from = Math.max(yrange.from, yrange.axis.min);
                      yrange.to = Math.min(yrange.to, yrange.axis.max);
  
                      var xequal = xrange.from === xrange.to,
                          yequal = yrange.from === yrange.to;
  
                      if (xequal && yequal) {
                          continue;
                      }
  
                      // then draw
                      xrange.from = Math.floor(xrange.axis.p2c(xrange.from));
                      xrange.to = Math.floor(xrange.axis.p2c(xrange.to));
                      yrange.from = Math.floor(yrange.axis.p2c(yrange.from));
                      yrange.to = Math.floor(yrange.axis.p2c(yrange.to));
  
                      if (xequal || yequal) {
                          var lineWidth = m.lineWidth || options.grid.markingsLineWidth,
                              subPixel = lineWidth % 2 ? 0.5 : 0;
                          ctx.beginPath();
                          ctx.strokeStyle = m.color || options.grid.markingsColor;
                          ctx.lineWidth = lineWidth;
                          if (xequal) {
                              ctx.moveTo(xrange.to + subPixel, yrange.from);
                              ctx.lineTo(xrange.to + subPixel, yrange.to);
                          } else {
                              ctx.moveTo(xrange.from, yrange.to + subPixel);
                              ctx.lineTo(xrange.to, yrange.to + subPixel);                            
                          }
                          ctx.stroke();
                      } else {
                          ctx.fillStyle = m.color || options.grid.markingsColor;
                          ctx.fillRect(xrange.from, yrange.to,
                                       xrange.to - xrange.from,
                                       yrange.from - yrange.to);
                      }
                  }
              }
  
              // draw the ticks
              axes = allAxes();
              bw = options.grid.borderWidth;
  
              for (var j = 0; j < axes.length; ++j) {
                  var axis = axes[j], box = axis.box,
                      t = axis.tickLength, x, y, xoff, yoff;
                  if (!axis.show || axis.ticks.length == 0)
                      continue;
  
                  ctx.lineWidth = 1;
  
                  // find the edges
                  if (axis.direction == "x") {
                      x = 0;
                      if (t == "full")
                          y = (axis.position == "top" ? 0 : plotHeight);
                      else
                          y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
                  }
                  else {
                      y = 0;
                      if (t == "full")
                          x = (axis.position == "left" ? 0 : plotWidth);
                      else
                          x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
                  }
  
                  // draw tick bar
                  if (!axis.innermost) {
                      ctx.strokeStyle = axis.options.color;
                      ctx.beginPath();
                      xoff = yoff = 0;
                      if (axis.direction == "x")
                          xoff = plotWidth + 1;
                      else
                          yoff = plotHeight + 1;
  
                      if (ctx.lineWidth == 1) {
                          if (axis.direction == "x") {
                              y = Math.floor(y) + 0.5;
                          } else {
                              x = Math.floor(x) + 0.5;
                          }
                      }
  
                      ctx.moveTo(x, y);
                      ctx.lineTo(x + xoff, y + yoff);
                      ctx.stroke();
                  }
  
                  // draw ticks
  
                  ctx.strokeStyle = axis.options.tickColor;
  
                  ctx.beginPath();
                  for (i = 0; i < axis.ticks.length; ++i) {
                      var v = axis.ticks[i].v;
  
                      xoff = yoff = 0;
  
                      if (isNaN(v) || v < axis.min || v > axis.max
                          // skip those lying on the axes if we got a border
                          || (t == "full"
                              && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0)
                              && (v == axis.min || v == axis.max)))
                          continue;
  
                      if (axis.direction == "x") {
                          x = axis.p2c(v);
                          yoff = t == "full" ? -plotHeight : t;
  
                          if (axis.position == "top")
                              yoff = -yoff;
                      }
                      else {
                          y = axis.p2c(v);
                          xoff = t == "full" ? -plotWidth : t;
  
                          if (axis.position == "left")
                              xoff = -xoff;
                      }
  
                      if (ctx.lineWidth == 1) {
                          if (axis.direction == "x")
                              x = Math.floor(x) + 0.5;
                          else
                              y = Math.floor(y) + 0.5;
                      }
  
                      ctx.moveTo(x, y);
                      ctx.lineTo(x + xoff, y + yoff);
                  }
  
                  ctx.stroke();
              }
  
  
              // draw border
              if (bw) {
                  // If either borderWidth or borderColor is an object, then draw the border
                  // line by line instead of as one rectangle
                  bc = options.grid.borderColor;
                  if(typeof bw == "object" || typeof bc == "object") {
                      if (typeof bw !== "object") {
                          bw = {top: bw, right: bw, bottom: bw, left: bw};
                      }
                      if (typeof bc !== "object") {
                          bc = {top: bc, right: bc, bottom: bc, left: bc};
                      }
  
                      if (bw.top > 0) {
                          ctx.strokeStyle = bc.top;
                          ctx.lineWidth = bw.top;
                          ctx.beginPath();
                          ctx.moveTo(0 - bw.left, 0 - bw.top/2);
                          ctx.lineTo(plotWidth, 0 - bw.top/2);
                          ctx.stroke();
                      }
  
                      if (bw.right > 0) {
                          ctx.strokeStyle = bc.right;
                          ctx.lineWidth = bw.right;
                          ctx.beginPath();
                          ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top);
                          ctx.lineTo(plotWidth + bw.right / 2, plotHeight);
                          ctx.stroke();
                      }
  
                      if (bw.bottom > 0) {
                          ctx.strokeStyle = bc.bottom;
                          ctx.lineWidth = bw.bottom;
                          ctx.beginPath();
                          ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2);
                          ctx.lineTo(0, plotHeight + bw.bottom / 2);
                          ctx.stroke();
                      }
  
                      if (bw.left > 0) {
                          ctx.strokeStyle = bc.left;
                          ctx.lineWidth = bw.left;
                          ctx.beginPath();
                          ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom);
                          ctx.lineTo(0- bw.left/2, 0);
                          ctx.stroke();
                      }
                  }
                  else {
                      ctx.lineWidth = bw;
                      ctx.strokeStyle = options.grid.borderColor;
                      ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
                  }
              }
  
              ctx.restore();
          }
  
          function drawAxisLabels() {
  
              $.each(allAxes(), function (_, axis) {
                  var box = axis.box,
                      legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
                      layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
                      font = axis.options.font || "flot-tick-label tickLabel",
                      tick, x, y, halign, valign;
  
                  // Remove text before checking for axis.show and ticks.length;
                  // otherwise plugins, like flot-tickrotor, that draw their own
                  // tick labels will end up with both theirs and the defaults.
  
                  surface.removeText(layer);
  
                  if (!axis.show || axis.ticks.length == 0)
                      return;
  
                  for (var i = 0; i < axis.ticks.length; ++i) {
  
                      tick = axis.ticks[i];
                      if (!tick.label || tick.v < axis.min || tick.v > axis.max)
                          continue;
  
                      if (axis.direction == "x") {
                          halign = "center";
                          x = plotOffset.left + axis.p2c(tick.v);
                          if (axis.position == "bottom") {
                              y = box.top + box.padding;
                          } else {
                              y = box.top + box.height - box.padding;
                              valign = "bottom";
                          }
                      } else {
                          valign = "middle";
                          y = plotOffset.top + axis.p2c(tick.v);
                          if (axis.position == "left") {
                              x = box.left + box.width - box.padding;
                              halign = "right";
                          } else {
                              x = box.left + box.padding;
                          }
                      }
  
                      surface.addText(layer, x, y, tick.label, font, null, null, halign, valign);
                  }
              });
          }
  
          function drawSeries(series) {
              if (series.lines.show)
                  drawSeriesLines(series);
              if (series.bars.show)
                  drawSeriesBars(series);
              if (series.points.show)
                  drawSeriesPoints(series);
          }
  
          function drawSeriesLines(series) {
              function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
                  var points = datapoints.points,
                      ps = datapoints.pointsize,
                      prevx = null, prevy = null;
  
                  ctx.beginPath();
                  for (var i = ps; i < points.length; i += ps) {
                      var x1 = points[i - ps], y1 = points[i - ps + 1],
                          x2 = points[i], y2 = points[i + 1];
  
                      if (x1 == null || x2 == null)
                          continue;
  
                      // clip with ymin
                      if (y1 <= y2 && y1 < axisy.min) {
                          if (y2 < axisy.min)
                              continue;   // line segment is outside
                          // compute new intersection point
                          x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y1 = axisy.min;
                      }
                      else if (y2 <= y1 && y2 < axisy.min) {
                          if (y1 < axisy.min)
                              continue;
                          x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y2 = axisy.min;
                      }
  
                      // clip with ymax
                      if (y1 >= y2 && y1 > axisy.max) {
                          if (y2 > axisy.max)
                              continue;
                          x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y1 = axisy.max;
                      }
                      else if (y2 >= y1 && y2 > axisy.max) {
                          if (y1 > axisy.max)
                              continue;
                          x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y2 = axisy.max;
                      }
  
                      // clip with xmin
                      if (x1 <= x2 && x1 < axisx.min) {
                          if (x2 < axisx.min)
                              continue;
                          y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x1 = axisx.min;
                      }
                      else if (x2 <= x1 && x2 < axisx.min) {
                          if (x1 < axisx.min)
                              continue;
                          y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x2 = axisx.min;
                      }
  
                      // clip with xmax
                      if (x1 >= x2 && x1 > axisx.max) {
                          if (x2 > axisx.max)
                              continue;
                          y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x1 = axisx.max;
                      }
                      else if (x2 >= x1 && x2 > axisx.max) {
                          if (x1 > axisx.max)
                              continue;
                          y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x2 = axisx.max;
                      }
  
                      if (x1 != prevx || y1 != prevy)
                          ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
  
                      prevx = x2;
                      prevy = y2;
                      ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
                  }
                  ctx.stroke();
              }
  
              function plotLineArea(datapoints, axisx, axisy) {
                  var points = datapoints.points,
                      ps = datapoints.pointsize,
                      bottom = Math.min(Math.max(0, axisy.min), axisy.max),
                      i = 0, top, areaOpen = false,
                      ypos = 1, segmentStart = 0, segmentEnd = 0;
  
                  // we process each segment in two turns, first forward
                  // direction to sketch out top, then once we hit the
                  // end we go backwards to sketch the bottom
                  while (true) {
                      if (ps > 0 && i > points.length + ps)
                          break;
  
                      i += ps; // ps is negative if going backwards
  
                      var x1 = points[i - ps],
                          y1 = points[i - ps + ypos],
                          x2 = points[i], y2 = points[i + ypos];
  
                      if (areaOpen) {
                          if (ps > 0 && x1 != null && x2 == null) {
                              // at turning point
                              segmentEnd = i;
                              ps = -ps;
                              ypos = 2;
                              continue;
                          }
  
                          if (ps < 0 && i == segmentStart + ps) {
                              // done with the reverse sweep
                              ctx.fill();
                              areaOpen = false;
                              ps = -ps;
                              ypos = 1;
                              i = segmentStart = segmentEnd + ps;
                              continue;
                          }
                      }
  
                      if (x1 == null || x2 == null)
                          continue;
  
                      // clip x values
  
                      // clip with xmin
                      if (x1 <= x2 && x1 < axisx.min) {
                          if (x2 < axisx.min)
                              continue;
                          y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x1 = axisx.min;
                      }
                      else if (x2 <= x1 && x2 < axisx.min) {
                          if (x1 < axisx.min)
                              continue;
                          y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x2 = axisx.min;
                      }
  
                      // clip with xmax
                      if (x1 >= x2 && x1 > axisx.max) {
                          if (x2 > axisx.max)
                              continue;
                          y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x1 = axisx.max;
                      }
                      else if (x2 >= x1 && x2 > axisx.max) {
                          if (x1 > axisx.max)
                              continue;
                          y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
                          x2 = axisx.max;
                      }
  
                      if (!areaOpen) {
                          // open area
                          ctx.beginPath();
                          ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
                          areaOpen = true;
                      }
  
                      // now first check the case where both is outside
                      if (y1 >= axisy.max && y2 >= axisy.max) {
                          ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
                          ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
                          continue;
                      }
                      else if (y1 <= axisy.min && y2 <= axisy.min) {
                          ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
                          ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
                          continue;
                      }
  
                      // else it's a bit more complicated, there might
                      // be a flat maxed out rectangle first, then a
                      // triangular cutout or reverse; to find these
                      // keep track of the current x values
                      var x1old = x1, x2old = x2;
  
                      // clip the y values, without shortcutting, we
                      // go through all cases in turn
  
                      // clip with ymin
                      if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
                          x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y1 = axisy.min;
                      }
                      else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
                          x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y2 = axisy.min;
                      }
  
                      // clip with ymax
                      if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
                          x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y1 = axisy.max;
                      }
                      else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
                          x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
                          y2 = axisy.max;
                      }
  
                      // if the x value was changed we got a rectangle
                      // to fill
                      if (x1 != x1old) {
                          ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
                          // it goes to (x1, y1), but we fill that below
                      }
  
                      // fill triangular section, this sometimes result
                      // in redundant points if (x1, y1) hasn't changed
                      // from previous line to, but we just ignore that
                      ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
                      ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
  
                      // fill the other rectangle if it's there
                      if (x2 != x2old) {
                          ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
                          ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
                      }
                  }
              }
  
              ctx.save();
              ctx.translate(plotOffset.left, plotOffset.top);
              ctx.lineJoin = "round";
  
              var lw = series.lines.lineWidth,
                  sw = series.shadowSize;
              // FIXME: consider another form of shadow when filling is turned on
              if (lw > 0 && sw > 0) {
                  // draw shadow as a thick and thin line with transparency
                  ctx.lineWidth = sw;
                  ctx.strokeStyle = "rgba(0,0,0,0.1)";
                  // position shadow at angle from the mid of line
                  var angle = Math.PI/18;
                  plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
                  ctx.lineWidth = sw/2;
                  plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
              }
  
              ctx.lineWidth = lw;
              ctx.strokeStyle = series.color;
              var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
              if (fillStyle) {
                  ctx.fillStyle = fillStyle;
                  plotLineArea(series.datapoints, series.xaxis, series.yaxis);
              }
  
              if (lw > 0)
                  plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
              ctx.restore();
          }
  
          function drawSeriesPoints(series) {
              function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
                  var points = datapoints.points, ps = datapoints.pointsize;
  
                  for (var i = 0; i < points.length; i += ps) {
                      var x = points[i], y = points[i + 1];
                      if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
                          continue;
  
                      ctx.beginPath();
                      x = axisx.p2c(x);
                      y = axisy.p2c(y) + offset;
                      if (symbol == "circle")
                          ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
                      else
                          symbol(ctx, x, y, radius, shadow);
                      ctx.closePath();
  
                      if (fillStyle) {
                          ctx.fillStyle = fillStyle;
                          ctx.fill();
                      }
                      ctx.stroke();
                  }
              }
  
              ctx.save();
              ctx.translate(plotOffset.left, plotOffset.top);
  
              var lw = series.points.lineWidth,
                  sw = series.shadowSize,
                  radius = series.points.radius,
                  symbol = series.points.symbol;
  
              // If the user sets the line width to 0, we change it to a very 
              // small value. A line width of 0 seems to force the default of 1.
              // Doing the conditional here allows the shadow setting to still be 
              // optional even with a lineWidth of 0.
  
              if( lw == 0 )
                  lw = 0.0001;
  
              if (lw > 0 && sw > 0) {
                  // draw shadow in two steps
                  var w = sw / 2;
                  ctx.lineWidth = w;
                  ctx.strokeStyle = "rgba(0,0,0,0.1)";
                  plotPoints(series.datapoints, radius, null, w + w/2, true,
                             series.xaxis, series.yaxis, symbol);
  
                  ctx.strokeStyle = "rgba(0,0,0,0.2)";
                  plotPoints(series.datapoints, radius, null, w/2, true,
                             series.xaxis, series.yaxis, symbol);
              }
  
              ctx.lineWidth = lw;
              ctx.strokeStyle = series.color;
              plotPoints(series.datapoints, radius,
                         getFillStyle(series.points, series.color), 0, false,
                         series.xaxis, series.yaxis, symbol);
              ctx.restore();
          }
  
          function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
              var left, right, bottom, top,
                  drawLeft, drawRight, drawTop, drawBottom,
                  tmp;
  
              // in horizontal mode, we start the bar from the left
              // instead of from the bottom so it appears to be
              // horizontal rather than vertical
              if (horizontal) {
                  drawBottom = drawRight = drawTop = true;
                  drawLeft = false;
                  left = b;
                  right = x;
                  top = y + barLeft;
                  bottom = y + barRight;
  
                  // account for negative bars
                  if (right < left) {
                      tmp = right;
                      right = left;
                      left = tmp;
                      drawLeft = true;
                      drawRight = false;
                  }
              }
              else {
                  drawLeft = drawRight = drawTop = true;
                  drawBottom = false;
                  left = x + barLeft;
                  right = x + barRight;
                  bottom = b;
                  top = y;
  
                  // account for negative bars
                  if (top < bottom) {
                      tmp = top;
                      top = bottom;
                      bottom = tmp;
                      drawBottom = true;
                      drawTop = false;
                  }
              }
  
              // clip
              if (right < axisx.min || left > axisx.max ||
                  top < axisy.min || bottom > axisy.max)
                  return;
  
              if (left < axisx.min) {
                  left = axisx.min;
                  drawLeft = false;
              }
  
              if (right > axisx.max) {
                  right = axisx.max;
                  drawRight = false;
              }
  
              if (bottom < axisy.min) {
                  bottom = axisy.min;
                  drawBottom = false;
              }
  
              if (top > axisy.max) {
                  top = axisy.max;
                  drawTop = false;
              }
  
              left = axisx.p2c(left);
              bottom = axisy.p2c(bottom);
              right = axisx.p2c(right);
              top = axisy.p2c(top);
  
              // fill the bar
              if (fillStyleCallback) {
                  c.fillStyle = fillStyleCallback(bottom, top);
                  c.fillRect(left, top, right - left, bottom - top)
              }
  
              // draw outline
              if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
                  c.beginPath();
  
                  // FIXME: inline moveTo is buggy with excanvas
                  c.moveTo(left, bottom);
                  if (drawLeft)
                      c.lineTo(left, top);
                  else
                      c.moveTo(left, top);
                  if (drawTop)
                      c.lineTo(right, top);
                  else
                      c.moveTo(right, top);
                  if (drawRight)
                      c.lineTo(right, bottom);
                  else
                      c.moveTo(right, bottom);
                  if (drawBottom)
                      c.lineTo(left, bottom);
                  else
                      c.moveTo(left, bottom);
                  c.stroke();
              }
          }
  
          function drawSeriesBars(series) {
              function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
                  var points = datapoints.points, ps = datapoints.pointsize;
  
                  for (var i = 0; i < points.length; i += ps) {
                      if (points[i] == null)
                          continue;
                      drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
                  }
              }
  
              ctx.save();
              ctx.translate(plotOffset.left, plotOffset.top);
  
              // FIXME: figure out a way to add shadows (for instance along the right edge)
              ctx.lineWidth = series.bars.lineWidth;
              ctx.strokeStyle = series.color;
  
              var barLeft;
  
              switch (series.bars.align) {
                  case "left":
                      barLeft = 0;
                      break;
                  case "right":
                      barLeft = -series.bars.barWidth;
                      break;
                  default:
                      barLeft = -series.bars.barWidth / 2;
              }
  
              var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
              plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis);
              ctx.restore();
          }
  
          function getFillStyle(filloptions, seriesColor, bottom, top) {
              var fill = filloptions.fill;
              if (!fill)
                  return null;
  
              if (filloptions.fillColor)
                  return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
  
              var c = $.color.parse(seriesColor);
              c.a = typeof fill == "number" ? fill : 0.4;
              c.normalize();
              return c.toString();
          }
  
          function insertLegend() {
  
              if (options.legend.container != null) {
                  $(options.legend.container).html("");
              } else {
                  placeholder.find(".legend").remove();
              }
  
              if (!options.legend.show) {
                  return;
              }
  
              var fragments = [], entries = [], rowStarted = false,
                  lf = options.legend.labelFormatter, s, label;
  
              // Build a list of legend entries, with each having a label and a color
  
              for (var i = 0; i < series.length; ++i) {
                  s = series[i];
                  if (s.label) {
                      label = lf ? lf(s.label, s) : s.label;
                      if (label) {
                          entries.push({
                              label: label,
                              color: s.color
                          });
                      }
                  }
              }
  
              // Sort the legend using either the default or a custom comparator
  
              if (options.legend.sorted) {
                  if ($.isFunction(options.legend.sorted)) {
                      entries.sort(options.legend.sorted);
                  } else if (options.legend.sorted == "reverse") {
                  	entries.reverse();
                  } else {
                      var ascending = options.legend.sorted != "descending";
                      entries.sort(function(a, b) {
                          return a.label == b.label ? 0 : (
                              (a.label < b.label) != ascending ? 1 : -1   // Logical XOR
                          );
                      });
                  }
              }
  
              // Generate markup for the list of entries, in their final order
  
              for (var i = 0; i < entries.length; ++i) {
  
                  var entry = entries[i];
  
                  if (i % options.legend.noColumns == 0) {
                      if (rowStarted)
                          fragments.push('</tr>');
                      fragments.push('<tr>');
                      rowStarted = true;
                  }
  
                  fragments.push(
                      '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' +
                      '<td class="legendLabel">' + entry.label + '</td>'
                  );
              }
  
              if (rowStarted)
                  fragments.push('</tr>');
  
              if (fragments.length == 0)
                  return;
  
              var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
              if (options.legend.container != null)
                  $(options.legend.container).html(table);
              else {
                  var pos = "",
                      p = options.legend.position,
                      m = options.legend.margin;
                  if (m[0] == null)
                      m = [m, m];
                  if (p.charAt(0) == "n")
                      pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
                  else if (p.charAt(0) == "s")
                      pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
                  if (p.charAt(1) == "e")
                      pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
                  else if (p.charAt(1) == "w")
                      pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
                  var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder);
                  if (options.legend.backgroundOpacity != 0.0) {
                      // put in the transparent background
                      // separately to avoid blended labels and
                      // label boxes
                      var c = options.legend.backgroundColor;
                      if (c == null) {
                          c = options.grid.backgroundColor;
                          if (c && typeof c == "string")
                              c = $.color.parse(c);
                          else
                              c = $.color.extract(legend, 'background-color');
                          c.a = 1;
                          c = c.toString();
                      }
                      var div = legend.children();
                      $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
                  }
              }
          }
  
  
          // interactive features
  
          var highlights = [],
              redrawTimeout = null;
  
          // returns the data item the mouse is over, or null if none is found
          function findNearbyItem(mouseX, mouseY, seriesFilter) {
              var maxDistance = options.grid.mouseActiveRadius,
                  smallestDistance = maxDistance * maxDistance + 1,
                  item = null, foundPoint = false, i, j, ps;
  
              for (i = series.length - 1; i >= 0; --i) {
                  if (!seriesFilter(series[i]))
                      continue;
  
                  var s = series[i],
                      axisx = s.xaxis,
                      axisy = s.yaxis,
                      points = s.datapoints.points,
                      mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
                      my = axisy.c2p(mouseY),
                      maxx = maxDistance / axisx.scale,
                      maxy = maxDistance / axisy.scale;
  
                  ps = s.datapoints.pointsize;
                  // with inverse transforms, we can't use the maxx/maxy
                  // optimization, sadly
                  if (axisx.options.inverseTransform)
                      maxx = Number.MAX_VALUE;
                  if (axisy.options.inverseTransform)
                      maxy = Number.MAX_VALUE;
  
                  if (s.lines.show || s.points.show) {
                      for (j = 0; j < points.length; j += ps) {
                          var x = points[j], y = points[j + 1];
                          if (x == null)
                              continue;
  
                          // For points and lines, the cursor must be within a
                          // certain distance to the data point
                          if (x - mx > maxx || x - mx < -maxx ||
                              y - my > maxy || y - my < -maxy)
                              continue;
  
                          // We have to calculate distances in pixels, not in
                          // data units, because the scales of the axes may be different
                          var dx = Math.abs(axisx.p2c(x) - mouseX),
                              dy = Math.abs(axisy.p2c(y) - mouseY),
                              dist = dx * dx + dy * dy; // we save the sqrt
  
                          // use <= to ensure last point takes precedence
                          // (last generally means on top of)
                          if (dist < smallestDistance) {
                              smallestDistance = dist;
                              item = [i, j / ps];
                          }
                      }
                  }
  
                  if (s.bars.show && !item) { // no other point can be nearby
  
                      var barLeft, barRight;
  
                      switch (s.bars.align) {
                          case "left":
                              barLeft = 0;
                              break;
                          case "right":
                              barLeft = -s.bars.barWidth;
                              break;
                          default:
                              barLeft = -s.bars.barWidth / 2;
                      }
  
                      barRight = barLeft + s.bars.barWidth;
  
                      for (j = 0; j < points.length; j += ps) {
                          var x = points[j], y = points[j + 1], b = points[j + 2];
                          if (x == null)
                              continue;
  
                          // for a bar graph, the cursor must be inside the bar
                          if (series[i].bars.horizontal ?
                              (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&
                               my >= y + barLeft && my <= y + barRight) :
                              (mx >= x + barLeft && mx <= x + barRight &&
                               my >= Math.min(b, y) && my <= Math.max(b, y)))
                                  item = [i, j / ps];
                      }
                  }
              }
  
              if (item) {
                  i = item[0];
                  j = item[1];
                  ps = series[i].datapoints.pointsize;
  
                  return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
                           dataIndex: j,
                           series: series[i],
                           seriesIndex: i };
              }
  
              return null;
          }
  
          function onMouseMove(e) {
              if (options.grid.hoverable)
                  triggerClickHoverEvent("plothover", e,
                                         function (s) { return s["hoverable"] != false; });
          }
  
          function onMouseLeave(e) {
              if (options.grid.hoverable)
                  triggerClickHoverEvent("plothover", e,
                                         function (s) { return false; });
          }
  
          function onClick(e) {
              triggerClickHoverEvent("plotclick", e,
                                     function (s) { return s["clickable"] != false; });
          }
  
          // trigger click or hover event (they send the same parameters
          // so we share their code)
          function triggerClickHoverEvent(eventname, event, seriesFilter) {
              var offset = eventHolder.offset(),
                  canvasX = event.pageX - offset.left - plotOffset.left,
                  canvasY = event.pageY - offset.top - plotOffset.top,
              pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
  
              pos.pageX = event.pageX;
              pos.pageY = event.pageY;
  
              var item = findNearbyItem(canvasX, canvasY, seriesFilter);
  
              if (item) {
                  // fill in mouse pos for any listeners out there
                  item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10);
                  item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10);
              }
  
              if (options.grid.autoHighlight) {
                  // clear auto-highlights
                  for (var i = 0; i < highlights.length; ++i) {
                      var h = highlights[i];
                      if (h.auto == eventname &&
                          !(item && h.series == item.series &&
                            h.point[0] == item.datapoint[0] &&
                            h.point[1] == item.datapoint[1]))
                          unhighlight(h.series, h.point);
                  }
  
                  if (item)
                      highlight(item.series, item.datapoint, eventname);
              }
  
              placeholder.trigger(eventname, [ pos, item ]);
          }
  
          function triggerRedrawOverlay() {
              var t = options.interaction.redrawOverlayInterval;
              if (t == -1) {      // skip event queue
                  drawOverlay();
                  return;
              }
  
              if (!redrawTimeout)
                  redrawTimeout = setTimeout(drawOverlay, t);
          }
  
          function drawOverlay() {
              redrawTimeout = null;
  
              // draw highlights
              octx.save();
              overlay.clear();
              octx.translate(plotOffset.left, plotOffset.top);
  
              var i, hi;
              for (i = 0; i < highlights.length; ++i) {
                  hi = highlights[i];
  
                  if (hi.series.bars.show)
                      drawBarHighlight(hi.series, hi.point);
                  else
                      drawPointHighlight(hi.series, hi.point);
              }
              octx.restore();
  
              executeHooks(hooks.drawOverlay, [octx]);
          }
  
          function highlight(s, point, auto) {
              if (typeof s == "number")
                  s = series[s];
  
              if (typeof point == "number") {
                  var ps = s.datapoints.pointsize;
                  point = s.datapoints.points.slice(ps * point, ps * (point + 1));
              }
  
              var i = indexOfHighlight(s, point);
              if (i == -1) {
                  highlights.push({ series: s, point: point, auto: auto });
  
                  triggerRedrawOverlay();
              }
              else if (!auto)
                  highlights[i].auto = false;
          }
  
          function unhighlight(s, point) {
              if (s == null && point == null) {
                  highlights = [];
                  triggerRedrawOverlay();
                  return;
              }
  
              if (typeof s == "number")
                  s = series[s];
  
              if (typeof point == "number") {
                  var ps = s.datapoints.pointsize;
                  point = s.datapoints.points.slice(ps * point, ps * (point + 1));
              }
  
              var i = indexOfHighlight(s, point);
              if (i != -1) {
                  highlights.splice(i, 1);
  
                  triggerRedrawOverlay();
              }
          }
  
          function indexOfHighlight(s, p) {
              for (var i = 0; i < highlights.length; ++i) {
                  var h = highlights[i];
                  if (h.series == s && h.point[0] == p[0]
                      && h.point[1] == p[1])
                      return i;
              }
              return -1;
          }
  
          function drawPointHighlight(series, point) {
              var x = point[0], y = point[1],
                  axisx = series.xaxis, axisy = series.yaxis,
                  highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString();
  
              if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
                  return;
  
              var pointRadius = series.points.radius + series.points.lineWidth / 2;
              octx.lineWidth = pointRadius;
              octx.strokeStyle = highlightColor;
              var radius = 1.5 * pointRadius;
              x = axisx.p2c(x);
              y = axisy.p2c(y);
  
              octx.beginPath();
              if (series.points.symbol == "circle")
                  octx.arc(x, y, radius, 0, 2 * Math.PI, false);
              else
                  series.points.symbol(octx, x, y, radius, false);
              octx.closePath();
              octx.stroke();
          }
  
          function drawBarHighlight(series, point) {
              var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(),
                  fillStyle = highlightColor,
                  barLeft;
  
              switch (series.bars.align) {
                  case "left":
                      barLeft = 0;
                      break;
                  case "right":
                      barLeft = -series.bars.barWidth;
                      break;
                  default:
                      barLeft = -series.bars.barWidth / 2;
              }
  
              octx.lineWidth = series.bars.lineWidth;
              octx.strokeStyle = highlightColor;
  
              drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
                      function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
          }
  
          function getColorOrGradient(spec, bottom, top, defaultColor) {
              if (typeof spec == "string")
                  return spec;
              else {
                  // assume this is a gradient spec; IE currently only
                  // supports a simple vertical gradient properly, so that's
                  // what we support too
                  var gradient = ctx.createLinearGradient(0, top, 0, bottom);
  
                  for (var i = 0, l = spec.colors.length; i < l; ++i) {
                      var c = spec.colors[i];
                      if (typeof c != "string") {
                          var co = $.color.parse(defaultColor);
                          if (c.brightness != null)
                              co = co.scale('rgb', c.brightness);
                          if (c.opacity != null)
                              co.a *= c.opacity;
                          c = co.toString();
                      }
                      gradient.addColorStop(i / (l - 1), c);
                  }
  
                  return gradient;
              }
          }
      }
  
      // Add the plot function to the top level of the jQuery object
  
      $.plot = function(placeholder, data, options) {
          //var t0 = new Date();
          var plot = new Plot($(placeholder), data, options, $.plot.plugins);
          //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
          return plot;
      };
  
      $.plot.version = "0.8.3";
  
      $.plot.plugins = [];
  
      // Also add the plot function as a chainable property
  
      $.fn.plot = function(data, options) {
          return this.each(function() {
              $.plot(this, data, options);
          });
      };
  
      // round to nearby lower multiple of base
      function floorInBase(n, base) {
          return base * Math.floor(n / base);
      }
  
  })(jQuery);
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/flot/jquery.flot.pie.js":
  /*!**********************************************!*\
    !*** ./node_modules/flot/jquery.flot.pie.js ***!
    \**********************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/* Flot plugin for rendering pie charts.
  
  Copyright (c) 2007-2014 IOLA and Ole Laursen.
  Licensed under the MIT license.
  
  The plugin assumes that each series has a single data value, and that each
  value is a positive integer or zero.  Negative numbers don't make sense for a
  pie chart, and have unpredictable results.  The values do NOT need to be
  passed in as percentages; the plugin will calculate the total and per-slice
  percentages internally.
  
  * Created by Brian Medendorp
  
  * Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars
  
  The plugin supports these options:
  
  	series: {
  		pie: {
  			show: true/false
  			radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
  			innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
  			startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
  			tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
  			offset: {
  				top: integer value to move the pie up or down
  				left: integer value to move the pie left or right, or 'auto'
  			},
  			stroke: {
  				color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
  				width: integer pixel width of the stroke
  			},
  			label: {
  				show: true/false, or 'auto'
  				formatter:  a user-defined function that modifies the text/style of the label text
  				radius: 0-1 for percentage of fullsize, or a specified pixel length
  				background: {
  					color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
  					opacity: 0-1
  				},
  				threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
  			},
  			combine: {
  				threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
  				color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
  				label: any text value of what the combined slice should be labeled
  			}
  			highlight: {
  				opacity: 0-1
  			}
  		}
  	}
  
  More detail and specific examples can be found in the included HTML file.
  
  */
  
  (function($) {
  
  	// Maximum redraw attempts when fitting labels within the plot
  
  	var REDRAW_ATTEMPTS = 10;
  
  	// Factor by which to shrink the pie when fitting labels within the plot
  
  	var REDRAW_SHRINK = 0.95;
  
  	function init(plot) {
  
  		var canvas = null,
  			target = null,
  			options = null,
  			maxRadius = null,
  			centerLeft = null,
  			centerTop = null,
  			processed = false,
  			ctx = null;
  
  		// interactive variables
  
  		var highlights = [];
  
  		// add hook to determine if pie plugin in enabled, and then perform necessary operations
  
  		plot.hooks.processOptions.push(function(plot, options) {
  			if (options.series.pie.show) {
  
  				options.grid.show = false;
  
  				// set labels.show
  
  				if (options.series.pie.label.show == "auto") {
  					if (options.legend.show) {
  						options.series.pie.label.show = false;
  					} else {
  						options.series.pie.label.show = true;
  					}
  				}
  
  				// set radius
  
  				if (options.series.pie.radius == "auto") {
  					if (options.series.pie.label.show) {
  						options.series.pie.radius = 3/4;
  					} else {
  						options.series.pie.radius = 1;
  					}
  				}
  
  				// ensure sane tilt
  
  				if (options.series.pie.tilt > 1) {
  					options.series.pie.tilt = 1;
  				} else if (options.series.pie.tilt < 0) {
  					options.series.pie.tilt = 0;
  				}
  			}
  		});
  
  		plot.hooks.bindEvents.push(function(plot, eventHolder) {
  			var options = plot.getOptions();
  			if (options.series.pie.show) {
  				if (options.grid.hoverable) {
  					eventHolder.unbind("mousemove").mousemove(onMouseMove);
  				}
  				if (options.grid.clickable) {
  					eventHolder.unbind("click").click(onClick);
  				}
  			}
  		});
  
  		plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) {
  			var options = plot.getOptions();
  			if (options.series.pie.show) {
  				processDatapoints(plot, series, data, datapoints);
  			}
  		});
  
  		plot.hooks.drawOverlay.push(function(plot, octx) {
  			var options = plot.getOptions();
  			if (options.series.pie.show) {
  				drawOverlay(plot, octx);
  			}
  		});
  
  		plot.hooks.draw.push(function(plot, newCtx) {
  			var options = plot.getOptions();
  			if (options.series.pie.show) {
  				draw(plot, newCtx);
  			}
  		});
  
  		function processDatapoints(plot, series, datapoints) {
  			if (!processed)	{
  				processed = true;
  				canvas = plot.getCanvas();
  				target = $(canvas).parent();
  				options = plot.getOptions();
  				plot.setData(combine(plot.getData()));
  			}
  		}
  
  		function combine(data) {
  
  			var total = 0,
  				combined = 0,
  				numCombined = 0,
  				color = options.series.pie.combine.color,
  				newdata = [];
  
  			// Fix up the raw data from Flot, ensuring the data is numeric
  
  			for (var i = 0; i < data.length; ++i) {
  
  				var value = data[i].data;
  
  				// If the data is an array, we'll assume that it's a standard
  				// Flot x-y pair, and are concerned only with the second value.
  
  				// Note how we use the original array, rather than creating a
  				// new one; this is more efficient and preserves any extra data
  				// that the user may have stored in higher indexes.
  
  				if ($.isArray(value) && value.length == 1) {
      				value = value[0];
  				}
  
  				if ($.isArray(value)) {
  					// Equivalent to $.isNumeric() but compatible with jQuery < 1.7
  					if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) {
  						value[1] = +value[1];
  					} else {
  						value[1] = 0;
  					}
  				} else if (!isNaN(parseFloat(value)) && isFinite(value)) {
  					value = [1, +value];
  				} else {
  					value = [1, 0];
  				}
  
  				data[i].data = [value];
  			}
  
  			// Sum up all the slices, so we can calculate percentages for each
  
  			for (var i = 0; i < data.length; ++i) {
  				total += data[i].data[0][1];
  			}
  
  			// Count the number of slices with percentages below the combine
  			// threshold; if it turns out to be just one, we won't combine.
  
  			for (var i = 0; i < data.length; ++i) {
  				var value = data[i].data[0][1];
  				if (value / total <= options.series.pie.combine.threshold) {
  					combined += value;
  					numCombined++;
  					if (!color) {
  						color = data[i].color;
  					}
  				}
  			}
  
  			for (var i = 0; i < data.length; ++i) {
  				var value = data[i].data[0][1];
  				if (numCombined < 2 || value / total > options.series.pie.combine.threshold) {
  					newdata.push(
  						$.extend(data[i], {     /* extend to allow keeping all other original data values
  						                           and using them e.g. in labelFormatter. */
  							data: [[1, value]],
  							color: data[i].color,
  							label: data[i].label,
  							angle: value * Math.PI * 2 / total,
  							percent: value / (total / 100)
  						})
  					);
  				}
  			}
  
  			if (numCombined > 1) {
  				newdata.push({
  					data: [[1, combined]],
  					color: color,
  					label: options.series.pie.combine.label,
  					angle: combined * Math.PI * 2 / total,
  					percent: combined / (total / 100)
  				});
  			}
  
  			return newdata;
  		}
  
  		function draw(plot, newCtx) {
  
  			if (!target) {
  				return; // if no series were passed
  			}
  
  			var canvasWidth = plot.getPlaceholder().width(),
  				canvasHeight = plot.getPlaceholder().height(),
  				legendWidth = target.children().filter(".legend").children().width() || 0;
  
  			ctx = newCtx;
  
  			// WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!
  
  			// When combining smaller slices into an 'other' slice, we need to
  			// add a new series.  Since Flot gives plugins no way to modify the
  			// list of series, the pie plugin uses a hack where the first call
  			// to processDatapoints results in a call to setData with the new
  			// list of series, then subsequent processDatapoints do nothing.
  
  			// The plugin-global 'processed' flag is used to control this hack;
  			// it starts out false, and is set to true after the first call to
  			// processDatapoints.
  
  			// Unfortunately this turns future setData calls into no-ops; they
  			// call processDatapoints, the flag is true, and nothing happens.
  
  			// To fix this we'll set the flag back to false here in draw, when
  			// all series have been processed, so the next sequence of calls to
  			// processDatapoints once again starts out with a slice-combine.
  			// This is really a hack; in 0.9 we need to give plugins a proper
  			// way to modify series before any processing begins.
  
  			processed = false;
  
  			// calculate maximum radius and center point
  
  			maxRadius =  Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
  			centerTop = canvasHeight / 2 + options.series.pie.offset.top;
  			centerLeft = canvasWidth / 2;
  
  			if (options.series.pie.offset.left == "auto") {
  				if (options.legend.position.match("w")) {
  					centerLeft += legendWidth / 2;
  				} else {
  					centerLeft -= legendWidth / 2;
  				}
  				if (centerLeft < maxRadius) {
  					centerLeft = maxRadius;
  				} else if (centerLeft > canvasWidth - maxRadius) {
  					centerLeft = canvasWidth - maxRadius;
  				}
  			} else {
  				centerLeft += options.series.pie.offset.left;
  			}
  
  			var slices = plot.getData(),
  				attempts = 0;
  
  			// Keep shrinking the pie's radius until drawPie returns true,
  			// indicating that all the labels fit, or we try too many times.
  
  			do {
  				if (attempts > 0) {
  					maxRadius *= REDRAW_SHRINK;
  				}
  				attempts += 1;
  				clear();
  				if (options.series.pie.tilt <= 0.8) {
  					drawShadow();
  				}
  			} while (!drawPie() && attempts < REDRAW_ATTEMPTS)
  
  			if (attempts >= REDRAW_ATTEMPTS) {
  				clear();
  				target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
  			}
  
  			if (plot.setSeries && plot.insertLegend) {
  				plot.setSeries(slices);
  				plot.insertLegend();
  			}
  
  			// we're actually done at this point, just defining internal functions at this point
  
  			function clear() {
  				ctx.clearRect(0, 0, canvasWidth, canvasHeight);
  				target.children().filter(".pieLabel, .pieLabelBackground").remove();
  			}
  
  			function drawShadow() {
  
  				var shadowLeft = options.series.pie.shadow.left;
  				var shadowTop = options.series.pie.shadow.top;
  				var edge = 10;
  				var alpha = options.series.pie.shadow.alpha;
  				var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
  
  				if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
  					return;	// shadow would be outside canvas, so don't draw it
  				}
  
  				ctx.save();
  				ctx.translate(shadowLeft,shadowTop);
  				ctx.globalAlpha = alpha;
  				ctx.fillStyle = "#000";
  
  				// center and rotate to starting position
  
  				ctx.translate(centerLeft,centerTop);
  				ctx.scale(1, options.series.pie.tilt);
  
  				//radius -= edge;
  
  				for (var i = 1; i <= edge; i++) {
  					ctx.beginPath();
  					ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
  					ctx.fill();
  					radius -= i;
  				}
  
  				ctx.restore();
  			}
  
  			function drawPie() {
  
  				var startAngle = Math.PI * options.series.pie.startAngle;
  				var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
  
  				// center and rotate to starting position
  
  				ctx.save();
  				ctx.translate(centerLeft,centerTop);
  				ctx.scale(1, options.series.pie.tilt);
  				//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
  
  				// draw slices
  
  				ctx.save();
  				var currentAngle = startAngle;
  				for (var i = 0; i < slices.length; ++i) {
  					slices[i].startAngle = currentAngle;
  					drawSlice(slices[i].angle, slices[i].color, true);
  				}
  				ctx.restore();
  
  				// draw slice outlines
  
  				if (options.series.pie.stroke.width > 0) {
  					ctx.save();
  					ctx.lineWidth = options.series.pie.stroke.width;
  					currentAngle = startAngle;
  					for (var i = 0; i < slices.length; ++i) {
  						drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
  					}
  					ctx.restore();
  				}
  
  				// draw donut hole
  
  				drawDonutHole(ctx);
  
  				ctx.restore();
  
  				// Draw the labels, returning true if they fit within the plot
  
  				if (options.series.pie.label.show) {
  					return drawLabels();
  				} else return true;
  
  				function drawSlice(angle, color, fill) {
  
  					if (angle <= 0 || isNaN(angle)) {
  						return;
  					}
  
  					if (fill) {
  						ctx.fillStyle = color;
  					} else {
  						ctx.strokeStyle = color;
  						ctx.lineJoin = "round";
  					}
  
  					ctx.beginPath();
  					if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
  						ctx.moveTo(0, 0); // Center of the pie
  					}
  
  					//ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
  					ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
  					ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
  					ctx.closePath();
  					//ctx.rotate(angle); // This doesn't work properly in Opera
  					currentAngle += angle;
  
  					if (fill) {
  						ctx.fill();
  					} else {
  						ctx.stroke();
  					}
  				}
  
  				function drawLabels() {
  
  					var currentAngle = startAngle;
  					var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
  
  					for (var i = 0; i < slices.length; ++i) {
  						if (slices[i].percent >= options.series.pie.label.threshold * 100) {
  							if (!drawLabel(slices[i], currentAngle, i)) {
  								return false;
  							}
  						}
  						currentAngle += slices[i].angle;
  					}
  
  					return true;
  
  					function drawLabel(slice, startAngle, index) {
  
  						if (slice.data[0][1] == 0) {
  							return true;
  						}
  
  						// format label text
  
  						var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
  
  						if (lf) {
  							text = lf(slice.label, slice);
  						} else {
  							text = slice.label;
  						}
  
  						if (plf) {
  							text = plf(text, slice);
  						}
  
  						var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
  						var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
  						var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
  
  						var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
  						target.append(html);
  
  						var label = target.children("#pieLabel" + index);
  						var labelTop = (y - label.height() / 2);
  						var labelLeft = (x - label.width() / 2);
  
  						label.css("top", labelTop);
  						label.css("left", labelLeft);
  
  						// check to make sure that the label is not outside the canvas
  
  						if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
  							return false;
  						}
  
  						if (options.series.pie.label.background.opacity != 0) {
  
  							// put in the transparent background separately to avoid blended labels and label boxes
  
  							var c = options.series.pie.label.background.color;
  
  							if (c == null) {
  								c = slice.color;
  							}
  
  							var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
  							$("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
  								.css("opacity", options.series.pie.label.background.opacity)
  								.insertBefore(label);
  						}
  
  						return true;
  					} // end individual label function
  				} // end drawLabels function
  			} // end drawPie function
  		} // end draw function
  
  		// Placed here because it needs to be accessed from multiple locations
  
  		function drawDonutHole(layer) {
  			if (options.series.pie.innerRadius > 0) {
  
  				// subtract the center
  
  				layer.save();
  				var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
  				layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
  				layer.beginPath();
  				layer.fillStyle = options.series.pie.stroke.color;
  				layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
  				layer.fill();
  				layer.closePath();
  				layer.restore();
  
  				// add inner stroke
  
  				layer.save();
  				layer.beginPath();
  				layer.strokeStyle = options.series.pie.stroke.color;
  				layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
  				layer.stroke();
  				layer.closePath();
  				layer.restore();
  
  				// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
  			}
  		}
  
  		//-- Additional Interactive related functions --
  
  		function isPointInPoly(poly, pt) {
  			for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
  				((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
  				&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
  				&& (c = !c);
  			return c;
  		}
  
  		function findNearbySlice(mouseX, mouseY) {
  
  			var slices = plot.getData(),
  				options = plot.getOptions(),
  				radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius,
  				x, y;
  
  			for (var i = 0; i < slices.length; ++i) {
  
  				var s = slices[i];
  
  				if (s.pie.show) {
  
  					ctx.save();
  					ctx.beginPath();
  					ctx.moveTo(0, 0); // Center of the pie
  					//ctx.scale(1, options.series.pie.tilt);	// this actually seems to break everything when here.
  					ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
  					ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
  					ctx.closePath();
  					x = mouseX - centerLeft;
  					y = mouseY - centerTop;
  
  					if (ctx.isPointInPath) {
  						if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
  							ctx.restore();
  							return {
  								datapoint: [s.percent, s.data],
  								dataIndex: 0,
  								series: s,
  								seriesIndex: i
  							};
  						}
  					} else {
  
  						// excanvas for IE doesn;t support isPointInPath, this is a workaround.
  
  						var p1X = radius * Math.cos(s.startAngle),
  							p1Y = radius * Math.sin(s.startAngle),
  							p2X = radius * Math.cos(s.startAngle + s.angle / 4),
  							p2Y = radius * Math.sin(s.startAngle + s.angle / 4),
  							p3X = radius * Math.cos(s.startAngle + s.angle / 2),
  							p3Y = radius * Math.sin(s.startAngle + s.angle / 2),
  							p4X = radius * Math.cos(s.startAngle + s.angle / 1.5),
  							p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5),
  							p5X = radius * Math.cos(s.startAngle + s.angle),
  							p5Y = radius * Math.sin(s.startAngle + s.angle),
  							arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]],
  							arrPoint = [x, y];
  
  						// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
  
  						if (isPointInPoly(arrPoly, arrPoint)) {
  							ctx.restore();
  							return {
  								datapoint: [s.percent, s.data],
  								dataIndex: 0,
  								series: s,
  								seriesIndex: i
  							};
  						}
  					}
  
  					ctx.restore();
  				}
  			}
  
  			return null;
  		}
  
  		function onMouseMove(e) {
  			triggerClickHoverEvent("plothover", e);
  		}
  
  		function onClick(e) {
  			triggerClickHoverEvent("plotclick", e);
  		}
  
  		// trigger click or hover event (they send the same parameters so we share their code)
  
  		function triggerClickHoverEvent(eventname, e) {
  
  			var offset = plot.offset();
  			var canvasX = parseInt(e.pageX - offset.left);
  			var canvasY =  parseInt(e.pageY - offset.top);
  			var item = findNearbySlice(canvasX, canvasY);
  
  			if (options.grid.autoHighlight) {
  
  				// clear auto-highlights
  
  				for (var i = 0; i < highlights.length; ++i) {
  					var h = highlights[i];
  					if (h.auto == eventname && !(item && h.series == item.series)) {
  						unhighlight(h.series);
  					}
  				}
  			}
  
  			// highlight the slice
  
  			if (item) {
  				highlight(item.series, eventname);
  			}
  
  			// trigger any hover bind events
  
  			var pos = { pageX: e.pageX, pageY: e.pageY };
  			target.trigger(eventname, [pos, item]);
  		}
  
  		function highlight(s, auto) {
  			//if (typeof s == "number") {
  			//	s = series[s];
  			//}
  
  			var i = indexOfHighlight(s);
  
  			if (i == -1) {
  				highlights.push({ series: s, auto: auto });
  				plot.triggerRedrawOverlay();
  			} else if (!auto) {
  				highlights[i].auto = false;
  			}
  		}
  
  		function unhighlight(s) {
  			if (s == null) {
  				highlights = [];
  				plot.triggerRedrawOverlay();
  			}
  
  			//if (typeof s == "number") {
  			//	s = series[s];
  			//}
  
  			var i = indexOfHighlight(s);
  
  			if (i != -1) {
  				highlights.splice(i, 1);
  				plot.triggerRedrawOverlay();
  			}
  		}
  
  		function indexOfHighlight(s) {
  			for (var i = 0; i < highlights.length; ++i) {
  				var h = highlights[i];
  				if (h.series == s)
  					return i;
  			}
  			return -1;
  		}
  
  		function drawOverlay(plot, octx) {
  
  			var options = plot.getOptions();
  
  			var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
  
  			octx.save();
  			octx.translate(centerLeft, centerTop);
  			octx.scale(1, options.series.pie.tilt);
  
  			for (var i = 0; i < highlights.length; ++i) {
  				drawHighlight(highlights[i].series);
  			}
  
  			drawDonutHole(octx);
  
  			octx.restore();
  
  			function drawHighlight(series) {
  
  				if (series.angle <= 0 || isNaN(series.angle)) {
  					return;
  				}
  
  				//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
  				octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
  				octx.beginPath();
  				if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
  					octx.moveTo(0, 0); // Center of the pie
  				}
  				octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
  				octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
  				octx.closePath();
  				octx.fill();
  			}
  		}
  	} // end init (plugin body)
  
  	// define pie specific options and their default values
  
  	var options = {
  		series: {
  			pie: {
  				show: false,
  				radius: "auto",	// actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
  				innerRadius: 0, /* for donut */
  				startAngle: 3/2,
  				tilt: 1,
  				shadow: {
  					left: 5,	// shadow left offset
  					top: 15,	// shadow top offset
  					alpha: 0.02	// shadow alpha
  				},
  				offset: {
  					top: 0,
  					left: "auto"
  				},
  				stroke: {
  					color: "#fff",
  					width: 1
  				},
  				label: {
  					show: "auto",
  					formatter: function(label, slice) {
  						return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
  					},	// formatter function
  					radius: 1,	// radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
  					background: {
  						color: null,
  						opacity: 0
  					},
  					threshold: 0	// percentage at which to hide the label (i.e. the slice is too narrow)
  				},
  				combine: {
  					threshold: -1,	// percentage at which to combine little slices into one larger slice
  					color: null,	// color to give the new slice (auto-generated if null)
  					label: "Other"	// label to give the new slice
  				},
  				highlight: {
  					//color: "#fff",		// will add this functionality once parseColor is available
  					opacity: 0.5
  				}
  			}
  		}
  	};
  
  	$.plot.plugins.push({
  		init: init,
  		options: options,
  		name: "pie",
  		version: "1.1"
  	});
  
  })(jQuery);
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/flot/jquery.flot.resize.js":
  /*!*************************************************!*\
    !*** ./node_modules/flot/jquery.flot.resize.js ***!
    \*************************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/*** IMPORTS FROM imports-loader ***/
  (function() {
  
  /* Flot plugin for automatically redrawing plots as the placeholder resizes.
  
  Copyright (c) 2007-2014 IOLA and Ole Laursen.
  Licensed under the MIT license.
  
  It works by listening for changes on the placeholder div (through the jQuery
  resize event plugin) - if the size changes, it will redraw the plot.
  
  There are no options. If you need to disable the plugin for some plots, you
  can just fix the size of their placeholders.
  
  */
  
  /* Inline dependency:
   * jQuery resize event - v1.1 - 3/14/2010
   * http://benalman.com/projects/jquery-resize-plugin/
   *
   * Copyright (c) 2010 "Cowboy" Ben Alman
   * Dual licensed under the MIT and GPL licenses.
   * http://benalman.com/about/license/
   */
  (function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this);
  
  (function ($) {
      var options = { }; // no options
  
      function init(plot) {
          function onResize() {
              var placeholder = plot.getPlaceholder();
  
              // somebody might have hidden us and we can't plot
              // when we don't have the dimensions
              if (placeholder.width() == 0 || placeholder.height() == 0)
                  return;
  
              plot.resize();
              plot.setupGrid();
              plot.draw();
          }
          
          function bindEvents(plot, eventHolder) {
              plot.getPlaceholder().resize(onResize);
          }
  
          function shutdown(plot, eventHolder) {
              plot.getPlaceholder().unbind("resize", onResize);
          }
          
          plot.hooks.bindEvents.push(bindEvents);
          plot.hooks.shutdown.push(shutdown);
      }
      
      $.plot.plugins.push({
          init: init,
          options: options,
          name: 'resize',
          version: '1.0'
      });
  })(jQuery);
  
  }.call(window));
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/flot/jquery.flot.time.js":
  /*!***********************************************!*\
    !*** ./node_modules/flot/jquery.flot.time.js ***!
    \***********************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/* Pretty handling of time axes.
  
  Copyright (c) 2007-2014 IOLA and Ole Laursen.
  Licensed under the MIT license.
  
  Set axis.mode to "time" to enable. See the section "Time series data" in
  API.txt for details.
  
  */
  
  (function($) {
  
  	var options = {
  		xaxis: {
  			timezone: null,		// "browser" for local to the client or timezone for timezone-js
  			timeformat: null,	// format string to use
  			twelveHourClock: false,	// 12 or 24 time in time mode
  			monthNames: null	// list of names of months
  		}
  	};
  
  	// round to nearby lower multiple of base
  
  	function floorInBase(n, base) {
  		return base * Math.floor(n / base);
  	}
  
  	// Returns a string with the date d formatted according to fmt.
  	// A subset of the Open Group's strftime format is supported.
  
  	function formatDate(d, fmt, monthNames, dayNames) {
  
  		if (typeof d.strftime == "function") {
  			return d.strftime(fmt);
  		}
  
  		var leftPad = function(n, pad) {
  			n = "" + n;
  			pad = "" + (pad == null ? "0" : pad);
  			return n.length == 1 ? pad + n : n;
  		};
  
  		var r = [];
  		var escape = false;
  		var hours = d.getHours();
  		var isAM = hours < 12;
  
  		if (monthNames == null) {
  			monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  		}
  
  		if (dayNames == null) {
  			dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  		}
  
  		var hours12;
  
  		if (hours > 12) {
  			hours12 = hours - 12;
  		} else if (hours == 0) {
  			hours12 = 12;
  		} else {
  			hours12 = hours;
  		}
  
  		for (var i = 0; i < fmt.length; ++i) {
  
  			var c = fmt.charAt(i);
  
  			if (escape) {
  				switch (c) {
  					case 'a': c = "" + dayNames[d.getDay()]; break;
  					case 'b': c = "" + monthNames[d.getMonth()]; break;
  					case 'd': c = leftPad(d.getDate()); break;
  					case 'e': c = leftPad(d.getDate(), " "); break;
  					case 'h':	// For back-compat with 0.7; remove in 1.0
  					case 'H': c = leftPad(hours); break;
  					case 'I': c = leftPad(hours12); break;
  					case 'l': c = leftPad(hours12, " "); break;
  					case 'm': c = leftPad(d.getMonth() + 1); break;
  					case 'M': c = leftPad(d.getMinutes()); break;
  					// quarters not in Open Group's strftime specification
  					case 'q':
  						c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
  					case 'S': c = leftPad(d.getSeconds()); break;
  					case 'y': c = leftPad(d.getFullYear() % 100); break;
  					case 'Y': c = "" + d.getFullYear(); break;
  					case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
  					case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
  					case 'w': c = "" + d.getDay(); break;
  				}
  				r.push(c);
  				escape = false;
  			} else {
  				if (c == "%") {
  					escape = true;
  				} else {
  					r.push(c);
  				}
  			}
  		}
  
  		return r.join("");
  	}
  
  	// To have a consistent view of time-based data independent of which time
  	// zone the client happens to be in we need a date-like object independent
  	// of time zones.  This is done through a wrapper that only calls the UTC
  	// versions of the accessor methods.
  
  	function makeUtcWrapper(d) {
  
  		function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
  			sourceObj[sourceMethod] = function() {
  				return targetObj[targetMethod].apply(targetObj, arguments);
  			};
  		};
  
  		var utc = {
  			date: d
  		};
  
  		// support strftime, if found
  
  		if (d.strftime != undefined) {
  			addProxyMethod(utc, "strftime", d, "strftime");
  		}
  
  		addProxyMethod(utc, "getTime", d, "getTime");
  		addProxyMethod(utc, "setTime", d, "setTime");
  
  		var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
  
  		for (var p = 0; p < props.length; p++) {
  			addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
  			addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
  		}
  
  		return utc;
  	};
  
  	// select time zone strategy.  This returns a date-like object tied to the
  	// desired timezone
  
  	function dateGenerator(ts, opts) {
  		if (opts.timezone == "browser") {
  			return new Date(ts);
  		} else if (!opts.timezone || opts.timezone == "utc") {
  			return makeUtcWrapper(new Date(ts));
  		} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
  			var d = new timezoneJS.Date();
  			// timezone-js is fickle, so be sure to set the time zone before
  			// setting the time.
  			d.setTimezone(opts.timezone);
  			d.setTime(ts);
  			return d;
  		} else {
  			return makeUtcWrapper(new Date(ts));
  		}
  	}
  	
  	// map of app. size of time units in milliseconds
  
  	var timeUnitSize = {
  		"second": 1000,
  		"minute": 60 * 1000,
  		"hour": 60 * 60 * 1000,
  		"day": 24 * 60 * 60 * 1000,
  		"month": 30 * 24 * 60 * 60 * 1000,
  		"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
  		"year": 365.2425 * 24 * 60 * 60 * 1000
  	};
  
  	// the allowed tick sizes, after 1 year we use
  	// an integer algorithm
  
  	var baseSpec = [
  		[1, "second"], [2, "second"], [5, "second"], [10, "second"],
  		[30, "second"], 
  		[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
  		[30, "minute"], 
  		[1, "hour"], [2, "hour"], [4, "hour"],
  		[8, "hour"], [12, "hour"],
  		[1, "day"], [2, "day"], [3, "day"],
  		[0.25, "month"], [0.5, "month"], [1, "month"],
  		[2, "month"]
  	];
  
  	// we don't know which variant(s) we'll need yet, but generating both is
  	// cheap
  
  	var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
  		[1, "year"]]);
  	var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
  		[1, "year"]]);
  
  	function init(plot) {
  		plot.hooks.processOptions.push(function (plot, options) {
  			$.each(plot.getAxes(), function(axisName, axis) {
  
  				var opts = axis.options;
  
  				if (opts.mode == "time") {
  					axis.tickGenerator = function(axis) {
  
  						var ticks = [];
  						var d = dateGenerator(axis.min, opts);
  						var minSize = 0;
  
  						// make quarter use a possibility if quarters are
  						// mentioned in either of these options
  
  						var spec = (opts.tickSize && opts.tickSize[1] ===
  							"quarter") ||
  							(opts.minTickSize && opts.minTickSize[1] ===
  							"quarter") ? specQuarters : specMonths;
  
  						if (opts.minTickSize != null) {
  							if (typeof opts.tickSize == "number") {
  								minSize = opts.tickSize;
  							} else {
  								minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
  							}
  						}
  
  						for (var i = 0; i < spec.length - 1; ++i) {
  							if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
  											  + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
  								&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
  								break;
  							}
  						}
  
  						var size = spec[i][0];
  						var unit = spec[i][1];
  
  						// special-case the possibility of several years
  
  						if (unit == "year") {
  
  							// if given a minTickSize in years, just use it,
  							// ensuring that it's an integer
  
  							if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
  								size = Math.floor(opts.minTickSize[0]);
  							} else {
  
  								var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
  								var norm = (axis.delta / timeUnitSize.year) / magn;
  
  								if (norm < 1.5) {
  									size = 1;
  								} else if (norm < 3) {
  									size = 2;
  								} else if (norm < 7.5) {
  									size = 5;
  								} else {
  									size = 10;
  								}
  
  								size *= magn;
  							}
  
  							// minimum size for years is 1
  
  							if (size < 1) {
  								size = 1;
  							}
  						}
  
  						axis.tickSize = opts.tickSize || [size, unit];
  						var tickSize = axis.tickSize[0];
  						unit = axis.tickSize[1];
  
  						var step = tickSize * timeUnitSize[unit];
  
  						if (unit == "second") {
  							d.setSeconds(floorInBase(d.getSeconds(), tickSize));
  						} else if (unit == "minute") {
  							d.setMinutes(floorInBase(d.getMinutes(), tickSize));
  						} else if (unit == "hour") {
  							d.setHours(floorInBase(d.getHours(), tickSize));
  						} else if (unit == "month") {
  							d.setMonth(floorInBase(d.getMonth(), tickSize));
  						} else if (unit == "quarter") {
  							d.setMonth(3 * floorInBase(d.getMonth() / 3,
  								tickSize));
  						} else if (unit == "year") {
  							d.setFullYear(floorInBase(d.getFullYear(), tickSize));
  						}
  
  						// reset smaller components
  
  						d.setMilliseconds(0);
  
  						if (step >= timeUnitSize.minute) {
  							d.setSeconds(0);
  						}
  						if (step >= timeUnitSize.hour) {
  							d.setMinutes(0);
  						}
  						if (step >= timeUnitSize.day) {
  							d.setHours(0);
  						}
  						if (step >= timeUnitSize.day * 4) {
  							d.setDate(1);
  						}
  						if (step >= timeUnitSize.month * 2) {
  							d.setMonth(floorInBase(d.getMonth(), 3));
  						}
  						if (step >= timeUnitSize.quarter * 2) {
  							d.setMonth(floorInBase(d.getMonth(), 6));
  						}
  						if (step >= timeUnitSize.year) {
  							d.setMonth(0);
  						}
  
  						var carry = 0;
  						var v = Number.NaN;
  						var prev;
  
  						do {
  
  							prev = v;
  							v = d.getTime();
  							ticks.push(v);
  
  							if (unit == "month" || unit == "quarter") {
  								if (tickSize < 1) {
  
  									// a bit complicated - we'll divide the
  									// month/quarter up but we need to take
  									// care of fractions so we don't end up in
  									// the middle of a day
  
  									d.setDate(1);
  									var start = d.getTime();
  									d.setMonth(d.getMonth() +
  										(unit == "quarter" ? 3 : 1));
  									var end = d.getTime();
  									d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
  									carry = d.getHours();
  									d.setHours(0);
  								} else {
  									d.setMonth(d.getMonth() +
  										tickSize * (unit == "quarter" ? 3 : 1));
  								}
  							} else if (unit == "year") {
  								d.setFullYear(d.getFullYear() + tickSize);
  							} else {
  								d.setTime(v + step);
  							}
  						} while (v < axis.max && v != prev);
  
  						return ticks;
  					};
  
  					axis.tickFormatter = function (v, axis) {
  
  						var d = dateGenerator(v, axis.options);
  
  						// first check global format
  
  						if (opts.timeformat != null) {
  							return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
  						}
  
  						// possibly use quarters if quarters are mentioned in
  						// any of these places
  
  						var useQuarters = (axis.options.tickSize &&
  								axis.options.tickSize[1] == "quarter") ||
  							(axis.options.minTickSize &&
  								axis.options.minTickSize[1] == "quarter");
  
  						var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
  						var span = axis.max - axis.min;
  						var suffix = (opts.twelveHourClock) ? " %p" : "";
  						var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
  						var fmt;
  
  						if (t < timeUnitSize.minute) {
  							fmt = hourCode + ":%M:%S" + suffix;
  						} else if (t < timeUnitSize.day) {
  							if (span < 2 * timeUnitSize.day) {
  								fmt = hourCode + ":%M" + suffix;
  							} else {
  								fmt = "%b %d " + hourCode + ":%M" + suffix;
  							}
  						} else if (t < timeUnitSize.month) {
  							fmt = "%b %d";
  						} else if ((useQuarters && t < timeUnitSize.quarter) ||
  							(!useQuarters && t < timeUnitSize.year)) {
  							if (span < timeUnitSize.year) {
  								fmt = "%b";
  							} else {
  								fmt = "%b %Y";
  							}
  						} else if (useQuarters && t < timeUnitSize.year) {
  							if (span < timeUnitSize.year) {
  								fmt = "Q%q";
  							} else {
  								fmt = "Q%q %Y";
  							}
  						} else {
  							fmt = "%Y";
  						}
  
  						var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
  
  						return rt;
  					};
  				}
  			});
  		});
  	}
  
  	$.plot.plugins.push({
  		init: init,
  		options: options,
  		name: 'time',
  		version: '1.0'
  	});
  
  	// Time-axis support used to be in Flot core, which exposed the
  	// formatDate function on the plot object.  Various plugins depend
  	// on the function, so we need to re-expose it here.
  
  	$.plot.formatDate = formatDate;
  	$.plot.dateGenerator = dateGenerator;
  
  })(jQuery);
  
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/jquery.flot.spline/jquery.flot.spline.js":
  /*!***************************************************************!*\
    !*** ./node_modules/jquery.flot.spline/jquery.flot.spline.js ***!
    \***************************************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/**
   * Flot plugin that provides spline interpolation for line graphs
   * author: Alex Bardas < alex.bardas@gmail.com >
   * modified by: Avi Kohn https://github.com/AMKohn
   * based on the spline interpolation described at:
   *		 http://scaledinnovation.com/analytics/splines/aboutSplines.html
   *
   * Example usage: (add in plot options series object)
   *		for linespline:
   *			series: {
   *				...
   *				lines: {
   *					show: false
   *				},
   *				splines: {
   *					show: true,
   *					tension: x, (float between 0 and 1, defaults to 0.5),
   *					lineWidth: y (number, defaults to 2),
   *					fill: z (float between 0 .. 1 or false, as in flot documentation)
   *				},
   *				...
   *			}
   *		areaspline:
   *			series: {
   *				...
   *				lines: {
   *					show: true,
   *					lineWidth: 0, (line drawing will not execute)
   *					fill: x, (float between 0 .. 1, as in flot documentation)
   *					...
   *				},
   *				splines: {
   *					show: true,
   *					tension: 0.5 (float between 0 and 1)
   *				},
   *				...
   *			}
   *
   */
  
  (function($) {
      'use strict'
  
      /**
       * @param {Number} x0, y0, x1, y1: coordinates of the end (knot) points of the segment
       * @param {Number} x2, y2: the next knot (not connected, but needed to calculate p2)
       * @param {Number} tension: control how far the control points spread
       * @return {Array}: p1 -> control point, from x1 back toward x0
       * 					p2 -> the next control point, returned to become the next segment's p1
       *
       * @api private
       */
      function getControlPoints(x0, y0, x1, y1, x2, y2, tension) {
  
          var pow = Math.pow,
              sqrt = Math.sqrt,
              d01, d12, fa, fb, p1x, p1y, p2x, p2y;
  
          //  Scaling factors: distances from this knot to the previous and following knots.
          d01 = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2));
          d12 = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
  
          fa = tension * d01 / (d01 + d12);
          fb = tension - fa;
  
          p1x = x1 + fa * (x0 - x2);
          p1y = y1 + fa * (y0 - y2);
  
          p2x = x1 - fb * (x0 - x2);
          p2y = y1 - fb * (y0 - y2);
  
          return [p1x, p1y, p2x, p2y];
      }
  
      var line = [];
  
      function drawLine(points, ctx, height, fill, seriesColor) {
          var c = $.color.parse(seriesColor);
  
          c.a = typeof fill == "number" ? fill : .3;
          c.normalize();
          c = c.toString();
  
          ctx.beginPath();
          ctx.moveTo(points[0][0], points[0][1]);
  
          var plength = points.length;
  
          for (var i = 0; i < plength; i++) {
              ctx[points[i][3]].apply(ctx, points[i][2]);
          }
  
          ctx.stroke();
  
          ctx.lineWidth = 0;
          ctx.lineTo(points[plength - 1][0], height);
          ctx.lineTo(points[0][0], height);
  
          ctx.closePath();
  
          if (fill !== false) {
              ctx.fillStyle = c;
              ctx.fill();
          }
      }
  
      /**
       * @param {Object} ctx: canvas context
       * @param {String} type: accepted strings: 'bezier' or 'quadratic' (defaults to quadratic)
       * @param {Array} points: 2 points for which to draw the interpolation
       * @param {Array} cpoints: control points for those segment points
       *
       * @api private
       */
      function queue(ctx, type, points, cpoints) {
          if (type === void 0 || (type !== 'bezier' && type !== 'quadratic')) {
              type = 'quadratic';
          }
          type = type + 'CurveTo';
  
          if (line.length == 0) line.push([points[0], points[1], cpoints.concat(points.slice(2)), type]);
          else if (type == "quadraticCurveTo" && points.length == 2) {
              cpoints = cpoints.slice(0, 2).concat(points);
  
              line.push([points[0], points[1], cpoints, type]);
          }
          else line.push([points[2], points[3], cpoints.concat(points.slice(2)), type]);
      }
  
      /**
       * @param {Object} plot
       * @param {Object} ctx: canvas context
       * @param {Object} series
       *
       * @api private
       */
  
      function drawSpline(plot, ctx, series) {
          // Not interested if spline is not requested
          if (series.splines.show !== true) {
              return;
          }
  
          var cp = [],
              // array of control points
              tension = series.splines.tension || 0.5,
              idx, x, y, points = series.datapoints.points,
              ps = series.datapoints.pointsize,
              plotOffset = plot.getPlotOffset(),
              len = points.length,
              pts = [];
  
          line = [];
  
          // Cannot display a linespline/areaspline if there are less than 3 points
          if (len / ps < 4) {
              $.extend(series.lines, series.splines);
              return;
          }
  
          for (idx = 0; idx < len; idx += ps) {
              x = points[idx];
              y = points[idx + 1];
              if (x == null || x < series.xaxis.min || x > series.xaxis.max || y < series.yaxis.min || y > series.yaxis.max) {
                  continue;
              }
  
              pts.push(series.xaxis.p2c(x) + plotOffset.left, series.yaxis.p2c(y) + plotOffset.top);
          }
  
          len = pts.length;
  
          // Draw an open curve, not connected at the ends
          for (idx = 0; idx < len - 2; idx += 2) {
              cp = cp.concat(getControlPoints.apply(this, pts.slice(idx, idx + 6).concat([tension])));
          }
  
          ctx.save();
          ctx.strokeStyle = series.color;
          ctx.lineWidth = series.splines.lineWidth;
  
          queue(ctx, 'quadratic', pts.slice(0, 4), cp.slice(0, 2));
  
          for (idx = 2; idx < len - 3; idx += 2) {
              queue(ctx, 'bezier', pts.slice(idx, idx + 4), cp.slice(2 * idx - 2, 2 * idx + 2));
          }
  
          queue(ctx, 'quadratic', pts.slice(len - 2, len), [cp[2 * len - 10], cp[2 * len - 9], pts[len - 4], pts[len - 3]]);
  
          drawLine(line, ctx, plot.height() + 10, series.splines.fill, series.color);
  
          ctx.restore();
      }
  
      $.plot.plugins.push({
          init: function(plot) {
              plot.hooks.drawSeries.push(drawSpline);
          },
          options: {
              series: {
                  splines: {
                      show: false,
                      lineWidth: 2,
                      tension: 0.5,
                      fill: false
                  }
              }
          },
          name: 'spline',
          version: '0.8.2'
      });
  })(jQuery);
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./node_modules/jquery.flot.tooltip/js/jquery.flot.tooltip.min.js":
  /*!************************************************************************!*\
    !*** ./node_modules/jquery.flot.tooltip/js/jquery.flot.tooltip.min.js ***!
    \************************************************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* WEBPACK VAR INJECTION */(function(jQuery) {/*
   * jquery.flot.tooltip
   * 
   * description: easy-to-use tooltips for Flot charts
   * version: 0.9.0
   * authors: Krzysztof Urbas @krzysu [myviews.pl],Evan Steinkerchner @Roundaround
   * website: https://github.com/krzysu/flot.tooltip
   * 
   * build on 2016-07-26
   * released under MIT License, 2012
  */ 
  !function(a){var b={tooltip:{show:!1,cssClass:"flotTip",content:"%s | X: %x | Y: %y",xDateFormat:null,yDateFormat:null,monthNames:null,dayNames:null,shifts:{x:10,y:20},defaultTheme:!0,snap:!0,lines:!1,clickTips:!1,onHover:function(a,b){},$compat:!1}};b.tooltipOpts=b.tooltip;var c=function(a){this.tipPosition={x:0,y:0},this.init(a)};c.prototype.init=function(b){function c(a){var c={};c.x=a.pageX,c.y=a.pageY,b.setTooltipPosition(c)}function d(c,d,g){f.clickmode?(a(b.getPlaceholder()).bind("plothover",e),b.hideTooltip(),f.clickmode=!1):(e(c,d,g),f.getDomElement().is(":visible")&&(a(b.getPlaceholder()).unbind("plothover",e),f.clickmode=!0))}function e(c,d,e){var g=function(a,b,c,d){return Math.sqrt((c-a)*(c-a)+(d-b)*(d-b))},h=function(a,b,c,d,e,f,h){if(!h||(h=function(a,b,c,d,e,f){if("undefined"!=typeof c)return{x:c,y:b};if("undefined"!=typeof d)return{x:a,y:d};var g,h=-1/((f-d)/(e-c));return{x:g=(e*(a*h-b+d)+c*(a*-h+b-f))/(h*(e-c)+d-f),y:h*g-h*a+b}}(a,b,c,d,e,f),h.x>=Math.min(c,e)&&h.x<=Math.max(c,e)&&h.y>=Math.min(d,f)&&h.y<=Math.max(d,f))){var i=d-f,j=e-c,k=c*f-d*e;return Math.abs(i*a+j*b+k)/Math.sqrt(i*i+j*j)}var l=g(a,b,c,d),m=g(a,b,e,f);return l>m?m:l};if(e)b.showTooltip(e,f.tooltipOptions.snap?e:d);else if(f.plotOptions.series.lines.show&&f.tooltipOptions.lines===!0){var i=f.plotOptions.grid.mouseActiveRadius,j={distance:i+1},k=d;a.each(b.getData(),function(a,c){for(var e=0,i=-1,l=1;l<c.data.length;l++)c.data[l-1][0]<=d.x&&c.data[l][0]>=d.x&&(e=l-1,i=l);if(-1===i)return void b.hideTooltip();var m={x:c.data[e][0],y:c.data[e][1]},n={x:c.data[i][0],y:c.data[i][1]},o=h(c.xaxis.p2c(d.x),c.yaxis.p2c(d.y),c.xaxis.p2c(m.x),c.yaxis.p2c(m.y),c.xaxis.p2c(n.x),c.yaxis.p2c(n.y),!1);if(o<j.distance){var p=g(m.x,m.y,d.x,d.y)<g(d.x,d.y,n.x,n.y)?e:i,q=(c.datapoints.pointsize,[d.x,m.y+(n.y-m.y)*((d.x-m.x)/(n.x-m.x))]),r={datapoint:q,dataIndex:p,series:c,seriesIndex:a};j={distance:o,item:r},f.tooltipOptions.snap&&(k={pageX:c.xaxis.p2c(q[0]),pageY:c.yaxis.p2c(q[1])})}}),j.distance<i+1?b.showTooltip(j.item,k):b.hideTooltip()}else b.hideTooltip()}var f=this,g=a.plot.plugins.length;if(this.plotPlugins=[],g)for(var h=0;g>h;h++)this.plotPlugins.push(a.plot.plugins[h].name);b.hooks.bindEvents.push(function(b,g){if(f.plotOptions=b.getOptions(),"boolean"==typeof f.plotOptions.tooltip&&(f.plotOptions.tooltipOpts.show=f.plotOptions.tooltip,f.plotOptions.tooltip=f.plotOptions.tooltipOpts,delete f.plotOptions.tooltipOpts),f.plotOptions.tooltip.show!==!1&&"undefined"!=typeof f.plotOptions.tooltip.show){f.tooltipOptions=f.plotOptions.tooltip,f.tooltipOptions.$compat?(f.wfunc="width",f.hfunc="height"):(f.wfunc="innerWidth",f.hfunc="innerHeight");f.getDomElement();a(b.getPlaceholder()).bind("plothover",e),f.tooltipOptions.clickTips&&a(b.getPlaceholder()).bind("plotclick",d),f.clickmode=!1,a(g).bind("mousemove",c)}}),b.hooks.shutdown.push(function(b,f){a(b.getPlaceholder()).unbind("plothover",e),a(b.getPlaceholder()).unbind("plotclick",d),b.removeTooltip(),a(f).unbind("mousemove",c)}),b.setTooltipPosition=function(b){var c=f.getDomElement(),d=c.outerWidth()+f.tooltipOptions.shifts.x,e=c.outerHeight()+f.tooltipOptions.shifts.y;b.x-a(window).scrollLeft()>a(window)[f.wfunc]()-d&&(b.x-=d,b.x=Math.max(b.x,0)),b.y-a(window).scrollTop()>a(window)[f.hfunc]()-e&&(b.y-=e),isNaN(b.x)?f.tipPosition.x=f.tipPosition.xPrev:(f.tipPosition.x=b.x,f.tipPosition.xPrev=b.x),isNaN(b.y)?f.tipPosition.y=f.tipPosition.yPrev:(f.tipPosition.y=b.y,f.tipPosition.yPrev=b.y)},b.showTooltip=function(a,c,d){var e=f.getDomElement(),g=f.stringFormat(f.tooltipOptions.content,a);""!==g&&(e.html(g),b.setTooltipPosition({x:f.tipPosition.x,y:f.tipPosition.y}),e.css({left:f.tipPosition.x+f.tooltipOptions.shifts.x,top:f.tipPosition.y+f.tooltipOptions.shifts.y}).show(),"function"==typeof f.tooltipOptions.onHover&&f.tooltipOptions.onHover(a,e))},b.hideTooltip=function(){f.getDomElement().hide().html("")},b.removeTooltip=function(){f.getDomElement().remove()}},c.prototype.getDomElement=function(){var b=a("<div>");return this.tooltipOptions&&this.tooltipOptions.cssClass&&(b=a("."+this.tooltipOptions.cssClass),0===b.length&&(b=a("<div />").addClass(this.tooltipOptions.cssClass),b.appendTo("body").hide().css({position:"absolute"}),this.tooltipOptions.defaultTheme&&b.css({background:"#fff","z-index":"1040",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111",display:"none","white-space":"nowrap"}))),b},c.prototype.stringFormat=function(a,b){var c,d,e,f,g,h=/%p\.{0,1}(\d{0,})/,i=/%s/,j=/%c/,k=/%lx/,l=/%ly/,m=/%x\.{0,1}(\d{0,})/,n=/%y\.{0,1}(\d{0,})/,o="%x",p="%y",q="%ct",r="%n";if("undefined"!=typeof b.series.threshold?(c=b.datapoint[0],d=b.datapoint[1],e=b.datapoint[2]):"undefined"!=typeof b.series.curvedLines?(c=b.datapoint[0],d=b.datapoint[1]):"undefined"!=typeof b.series.lines&&b.series.lines.steps?(c=b.series.datapoints.points[2*b.dataIndex],d=b.series.datapoints.points[2*b.dataIndex+1],e=""):(c=b.series.data[b.dataIndex][0],d=b.series.data[b.dataIndex][1],e=b.series.data[b.dataIndex][2]),null===b.series.label&&b.series.originSeries&&(b.series.label=b.series.originSeries.label),"function"==typeof a&&(a=a(b.series.label,c,d,b)),"boolean"==typeof a&&!a)return"";if(e&&(a=a.replace(q,e)),"undefined"!=typeof b.series.percent?f=b.series.percent:"undefined"!=typeof b.series.percents&&(f=b.series.percents[b.dataIndex]),"number"==typeof f&&(a=this.adjustValPrecision(h,a,f)),b.series.hasOwnProperty("pie")&&"undefined"!=typeof b.series.data[0][1]&&(g=b.series.data[0][1]),"number"==typeof g&&(a=a.replace(r,g)),a="undefined"!=typeof b.series.label?a.replace(i,b.series.label):a.replace(i,""),a="undefined"!=typeof b.series.color?a.replace(j,b.series.color):a.replace(j,""),a=this.hasAxisLabel("xaxis",b)?a.replace(k,b.series.xaxis.options.axisLabel):a.replace(k,""),a=this.hasAxisLabel("yaxis",b)?a.replace(l,b.series.yaxis.options.axisLabel):a.replace(l,""),this.isTimeMode("xaxis",b)&&this.isXDateFormat(b)&&(a=a.replace(m,this.timestampToDate(c,this.tooltipOptions.xDateFormat,b.series.xaxis.options))),this.isTimeMode("yaxis",b)&&this.isYDateFormat(b)&&(a=a.replace(n,this.timestampToDate(d,this.tooltipOptions.yDateFormat,b.series.yaxis.options))),"number"==typeof c&&(a=this.adjustValPrecision(m,a,c)),"number"==typeof d&&(a=this.adjustValPrecision(n,a,d)),"undefined"!=typeof b.series.xaxis.ticks){var s;s=this.hasRotatedXAxisTicks(b)?"rotatedTicks":"ticks";var t=b.dataIndex+b.seriesIndex;for(var u in b.series.xaxis[s])if(b.series.xaxis[s].hasOwnProperty(t)&&!this.isTimeMode("xaxis",b)){var v=this.isCategoriesMode("xaxis",b)?b.series.xaxis[s][t].label:b.series.xaxis[s][t].v;v===c&&(a=a.replace(m,b.series.xaxis[s][t].label.replace(/\$/g,"$$$$")))}}if("undefined"!=typeof b.series.yaxis.ticks)for(var w in b.series.yaxis.ticks)if(b.series.yaxis.ticks.hasOwnProperty(w)){var x=this.isCategoriesMode("yaxis",b)?b.series.yaxis.ticks[w].label:b.series.yaxis.ticks[w].v;x===d&&(a=a.replace(n,b.series.yaxis.ticks[w].label.replace(/\$/g,"$$$$")))}return"undefined"!=typeof b.series.xaxis.tickFormatter&&(a=a.replace(o,b.series.xaxis.tickFormatter(c,b.series.xaxis).replace(/\$/g,"$$"))),"undefined"!=typeof b.series.yaxis.tickFormatter&&(a=a.replace(p,b.series.yaxis.tickFormatter(d,b.series.yaxis).replace(/\$/g,"$$"))),a},c.prototype.isTimeMode=function(a,b){return"undefined"!=typeof b.series[a].options.mode&&"time"===b.series[a].options.mode},c.prototype.isXDateFormat=function(a){return"undefined"!=typeof this.tooltipOptions.xDateFormat&&null!==this.tooltipOptions.xDateFormat},c.prototype.isYDateFormat=function(a){return"undefined"!=typeof this.tooltipOptions.yDateFormat&&null!==this.tooltipOptions.yDateFormat},c.prototype.isCategoriesMode=function(a,b){return"undefined"!=typeof b.series[a].options.mode&&"categories"===b.series[a].options.mode},c.prototype.timestampToDate=function(b,c,d){var e=a.plot.dateGenerator(b,d);return a.plot.formatDate(e,c,this.tooltipOptions.monthNames,this.tooltipOptions.dayNames)},c.prototype.adjustValPrecision=function(a,b,c){var d,e=b.match(a);return null!==e&&""!==RegExp.$1&&(d=RegExp.$1,c=c.toFixed(d),b=b.replace(a,c)),b},c.prototype.hasAxisLabel=function(b,c){return-1!==a.inArray("axisLabels",this.plotPlugins)&&"undefined"!=typeof c.series[b].options.axisLabel&&c.series[b].options.axisLabel.length>0},c.prototype.hasRotatedXAxisTicks=function(b){return-1!==a.inArray("tickRotor",this.plotPlugins)&&"undefined"!=typeof b.series.xaxis.rotatedTicks};var d=function(a){new c(a)};a.plot.plugins.push({init:d,options:b,name:"tooltip",version:"0.8.5"})}(jQuery);
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ "./resources/js/angle/modules/charts/flot.js":
  /*!***************************************************!*\
    !*** ./resources/js/angle/modules/charts/flot.js ***!
    \***************************************************/
  /*! no exports provided */
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
  
  "use strict";
  __webpack_require__.r(__webpack_exports__);
  /* WEBPACK VAR INJECTION */(function($) {/* harmony import */ var flot_jquery_flot_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! flot/jquery.flot.js */ "./node_modules/flot/jquery.flot.js");
  /* harmony import */ var flot_jquery_flot_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(flot_jquery_flot_js__WEBPACK_IMPORTED_MODULE_0__);
  /* harmony import */ var flot_jquery_flot_categories_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! flot/jquery.flot.categories.js */ "./node_modules/flot/jquery.flot.categories.js");
  /* harmony import */ var flot_jquery_flot_categories_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(flot_jquery_flot_categories_js__WEBPACK_IMPORTED_MODULE_1__);
  /* harmony import */ var flot_jquery_flot_pie_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! flot/jquery.flot.pie.js */ "./node_modules/flot/jquery.flot.pie.js");
  /* harmony import */ var flot_jquery_flot_pie_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(flot_jquery_flot_pie_js__WEBPACK_IMPORTED_MODULE_2__);
  /* harmony import */ var flot_jquery_flot_resize_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! flot/jquery.flot.resize.js */ "./node_modules/flot/jquery.flot.resize.js");
  /* harmony import */ var flot_jquery_flot_resize_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(flot_jquery_flot_resize_js__WEBPACK_IMPORTED_MODULE_3__);
  /* harmony import */ var flot_jquery_flot_time_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! flot/jquery.flot.time.js */ "./node_modules/flot/jquery.flot.time.js");
  /* harmony import */ var flot_jquery_flot_time_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(flot_jquery_flot_time_js__WEBPACK_IMPORTED_MODULE_4__);
  /* harmony import */ var jquery_flot_spline_jquery_flot_spline_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! jquery.flot.spline/jquery.flot.spline.js */ "./node_modules/jquery.flot.spline/jquery.flot.spline.js");
  /* harmony import */ var jquery_flot_spline_jquery_flot_spline_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(jquery_flot_spline_jquery_flot_spline_js__WEBPACK_IMPORTED_MODULE_5__);
  /* harmony import */ var jquery_flot_tooltip_js_jquery_flot_tooltip_min_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! jquery.flot.tooltip/js/jquery.flot.tooltip.min.js */ "./node_modules/jquery.flot.tooltip/js/jquery.flot.tooltip.min.js");
  /* harmony import */ var jquery_flot_tooltip_js_jquery_flot_tooltip_min_js__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(jquery_flot_tooltip_js_jquery_flot_tooltip_min_js__WEBPACK_IMPORTED_MODULE_6__);
  // CHART SPLINE
  // -----------------------------------
  // Flot Charts
  
  
  
  
  
  
  
  
  (function () {
    'use strict';
  
    $(initFlotSpline);
  
    function initFlotSpline() {
      var data = [{
        "label": "Uniques",
        "color": "#768294",
        "data": [["Mar", 70], ["Apr", 85], ["May", 59], ["Jun", 93], ["Jul", 66], ["Aug", 86], ["Sep", 60]]
      }, {
        "label": "Recurrent",
        "color": "#1f92fe",
        "data": [["Mar", 21], ["Apr", 12], ["May", 27], ["Jun", 24], ["Jul", 16], ["Aug", 39], ["Sep", 15]]
      }];
      var datav2 = [{
        "label": "Hours",
        "color": "#23b7e5",
        "data": [["Jan", 70], ["Feb", 20], ["Mar", 70], ["Apr", 85], ["May", 59], ["Jun", 93], ["Jul", 66], ["Aug", 86], ["Sep", 60], ["Oct", 60], ["Nov", 12], ["Dec", 50]]
      }, {
        "label": "Commits",
        "color": "#7266ba",
        "data": [["Jan", 20], ["Feb", 70], ["Mar", 30], ["Apr", 50], ["May", 85], ["Jun", 43], ["Jul", 96], ["Aug", 36], ["Sep", 80], ["Oct", 10], ["Nov", 72], ["Dec", 31]]
      }];
      var datav3 = [{
        "label": "Home",
        "color": "#1ba3cd",
        "data": [["1", 38], ["2", 40], ["3", 42], ["4", 48], ["5", 50], ["6", 70], ["7", 145], ["8", 70], ["9", 59], ["10", 48], ["11", 38], ["12", 29], ["13", 30], ["14", 22], ["15", 28]]
      }, {
        "label": "Overall",
        "color": "#3a3f51",
        "data": [["1", 16], ["2", 18], ["3", 17], ["4", 16], ["5", 30], ["6", 110], ["7", 19], ["8", 18], ["9", 110], ["10", 19], ["11", 16], ["12", 10], ["13", 20], ["14", 10], ["15", 20]]
      }];
      var options = {
        series: {
          lines: {
            show: false
          },
          points: {
            show: true,
            radius: 4
          },
          splines: {
            show: true,
            tension: 0.4,
            lineWidth: 1,
            fill: 0.5
          }
        },
        grid: {
          borderColor: '#eee',
          borderWidth: 1,
          hoverable: true,
          backgroundColor: '#fcfcfc'
        },
        tooltip: true,
        tooltipOpts: {
          content: function content(label, x, y) {
            return x + ' : ' + y;
          }
        },
        xaxis: {
          tickColor: '#fcfcfc',
          mode: 'categories'
        },
        yaxis: {
          min: 0,
          max: 150,
          // optional: use it for a clear represetation
          tickColor: '#eee',
          //position: 'right' or 'left',
          tickFormatter: function tickFormatter(v) {
            return v
            /* + ' visitors'*/
            ;
          }
        },
        shadowSize: 0
      };
      var chart = $('.chart-spline');
      if (chart.length) $.plot(chart, data, options);
      var chartv2 = $('.chart-splinev2');
      if (chartv2.length) $.plot(chartv2, datav2, options);
      var chartv3 = $('.chart-splinev3');
      if (chartv3.length) $.plot(chartv3, datav3, options);
    }
  })(); // CHART AREA
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotArea);
  
    function initFlotArea() {
      var data = [{
        "label": "Uniques",
        "color": "#aad874",
        "data": [["Mar", 50], ["Apr", 84], ["May", 52], ["Jun", 88], ["Jul", 69], ["Aug", 92], ["Sep", 58]]
      }, {
        "label": "Recurrent",
        "color": "#7dc7df",
        "data": [["Mar", 13], ["Apr", 44], ["May", 44], ["Jun", 27], ["Jul", 38], ["Aug", 11], ["Sep", 39]]
      }];
      var options = {
        series: {
          lines: {
            show: true,
            fill: 0.8
          },
          points: {
            show: true,
            radius: 4
          }
        },
        grid: {
          borderColor: '#eee',
          borderWidth: 1,
          hoverable: true,
          backgroundColor: '#fcfcfc'
        },
        tooltip: true,
        tooltipOpts: {
          content: function content(label, x, y) {
            return x + ' : ' + y;
          }
        },
        xaxis: {
          tickColor: '#fcfcfc',
          mode: 'categories'
        },
        yaxis: {
          min: 0,
          tickColor: '#eee',
          // position: 'right' or 'left'
          tickFormatter: function tickFormatter(v) {
            return v + ' visitors';
          }
        },
        shadowSize: 0
      };
      var chart = $('.chart-area');
      if (chart.length) $.plot(chart, data, options);
    }
  })(); // CHART BAR
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotBar);
  
    function initFlotBar() {
      var data = [{
        "label": "Sales",
        "color": "#9cd159",
        "data": [["Jan", 27], ["Feb", 82], ["Mar", 56], ["Apr", 14], ["May", 28], ["Jun", 77], ["Jul", 23], ["Aug", 49], ["Sep", 81], ["Oct", 20]]
      }];
      var options = {
        series: {
          bars: {
            align: 'center',
            lineWidth: 0,
            show: true,
            barWidth: 0.6,
            fill: 0.9
          }
        },
        grid: {
          borderColor: '#eee',
          borderWidth: 1,
          hoverable: true,
          backgroundColor: '#fcfcfc'
        },
        tooltip: true,
        tooltipOpts: {
          content: function content(label, x, y) {
            return x + ' : ' + y;
          }
        },
        xaxis: {
          tickColor: '#fcfcfc',
          mode: 'categories'
        },
        yaxis: {
          // position: 'right' or 'left'
          tickColor: '#eee'
        },
        shadowSize: 0
      };
      var chart = $('.chart-bar');
      if (chart.length) $.plot(chart, data, options);
    }
  })(); // CHART BAR STACKED
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotBarStacked);
  
    function initFlotBarStacked() {
      var data = [{
        "label": "Tweets",
        "color": "#51bff2",
        "data": [["Jan", 56], ["Feb", 81], ["Mar", 97], ["Apr", 44], ["May", 24], ["Jun", 85], ["Jul", 94], ["Aug", 78], ["Sep", 52], ["Oct", 17], ["Nov", 90], ["Dec", 62]]
      }, {
        "label": "Likes",
        "color": "#4a8ef1",
        "data": [["Jan", 69], ["Feb", 135], ["Mar", 14], ["Apr", 100], ["May", 100], ["Jun", 62], ["Jul", 115], ["Aug", 22], ["Sep", 104], ["Oct", 132], ["Nov", 72], ["Dec", 61]]
      }, {
        "label": "+1",
        "color": "#f0693a",
        "data": [["Jan", 29], ["Feb", 36], ["Mar", 47], ["Apr", 21], ["May", 5], ["Jun", 49], ["Jul", 37], ["Aug", 44], ["Sep", 28], ["Oct", 9], ["Nov", 12], ["Dec", 35]]
      }];
      var datav2 = [{
        "label": "Pending",
        "color": "#9289ca",
        "data": [["Pj1", 86], ["Pj2", 136], ["Pj3", 97], ["Pj4", 110], ["Pj5", 62], ["Pj6", 85], ["Pj7", 115], ["Pj8", 78], ["Pj9", 104], ["Pj10", 82], ["Pj11", 97], ["Pj12", 110], ["Pj13", 62]]
      }, {
        "label": "Assigned",
        "color": "#7266ba",
        "data": [["Pj1", 49], ["Pj2", 81], ["Pj3", 47], ["Pj4", 44], ["Pj5", 100], ["Pj6", 49], ["Pj7", 94], ["Pj8", 44], ["Pj9", 52], ["Pj10", 17], ["Pj11", 47], ["Pj12", 44], ["Pj13", 100]]
      }, {
        "label": "Completed",
        "color": "#564aa3",
        "data": [["Pj1", 29], ["Pj2", 56], ["Pj3", 14], ["Pj4", 21], ["Pj5", 5], ["Pj6", 24], ["Pj7", 37], ["Pj8", 22], ["Pj9", 28], ["Pj10", 9], ["Pj11", 14], ["Pj12", 21], ["Pj13", 5]]
      }];
      var options = {
        series: {
          stack: true,
          bars: {
            align: 'center',
            lineWidth: 0,
            show: true,
            barWidth: 0.6,
            fill: 0.9
          }
        },
        grid: {
          borderColor: '#eee',
          borderWidth: 1,
          hoverable: true,
          backgroundColor: '#fcfcfc'
        },
        tooltip: true,
        tooltipOpts: {
          content: function content(label, x, y) {
            return x + ' : ' + y;
          }
        },
        xaxis: {
          tickColor: '#fcfcfc',
          mode: 'categories'
        },
        yaxis: {
          // position: 'right' or 'left'
          tickColor: '#eee'
        },
        shadowSize: 0
      };
      var chart = $('.chart-bar-stacked');
      if (chart.length) $.plot(chart, data, options);
      var chartv2 = $('.chart-bar-stackedv2');
      if (chartv2.length) $.plot(chartv2, datav2, options);
    }
  })(); // CHART DONUT
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotDonut);
  
    function initFlotDonut() {
      var data = [{
        "color": "#39C558",
        "data": 60,
        "label": "Coffee"
      }, {
        "color": "#00b4ff",
        "data": 90,
        "label": "CSS"
      }, {
        "color": "#FFBE41",
        "data": 50,
        "label": "LESS"
      }, {
        "color": "#ff3e43",
        "data": 80,
        "label": "Jade"
      }, {
        "color": "#937fc7",
        "data": 116,
        "label": "AngularJS"
      }];
      var options = {
        series: {
          pie: {
            show: true,
            innerRadius: 0.5 // This makes the donut shape
  
          }
        }
      };
      var chart = $('.chart-donut');
      if (chart.length) $.plot(chart, data, options);
    }
  })(); // CHART LINE
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotLine);
  
    function initFlotLine() {
      var data = [{
        "label": "Complete",
        "color": "#5ab1ef",
        "data": [["Jan", 188], ["Feb", 183], ["Mar", 185], ["Apr", 199], ["May", 190], ["Jun", 194], ["Jul", 194], ["Aug", 184], ["Sep", 74]]
      }, {
        "label": "In Progress",
        "color": "#f5994e",
        "data": [["Jan", 153], ["Feb", 116], ["Mar", 136], ["Apr", 119], ["May", 148], ["Jun", 133], ["Jul", 118], ["Aug", 161], ["Sep", 59]]
      }, {
        "label": "Cancelled",
        "color": "#d87a80",
        "data": [["Jan", 111], ["Feb", 97], ["Mar", 93], ["Apr", 110], ["May", 102], ["Jun", 93], ["Jul", 92], ["Aug", 92], ["Sep", 44]]
      }];
      var options = {
        series: {
          lines: {
            show: true,
            fill: 0.01
          },
          points: {
            show: true,
            radius: 4
          }
        },
        grid: {
          borderColor: '#eee',
          borderWidth: 1,
          hoverable: true,
          backgroundColor: '#fcfcfc'
        },
        tooltip: true,
        tooltipOpts: {
          content: function content(label, x, y) {
            return x + ' : ' + y;
          }
        },
        xaxis: {
          tickColor: '#eee',
          mode: 'categories'
        },
        yaxis: {
          // position: 'right' or 'left'
          tickColor: '#eee'
        },
        shadowSize: 0
      };
      var chart = $('.chart-line');
      if (chart.length) $.plot(chart, data, options);
    }
  })(); // CHART PIE
  // -----------------------------------
  
  
  (function () {
    'use strict';
  
    $(initFlotPie);
  
    function initFlotPie() {
      var data = [{
        "label": "jQuery",
        "color": "#4acab4",
        "data": 30
      }, {
        "label": "CSS",
        "color": "#ffea88",
        "data": 40
      }, {
        "label": "LESS",
        "color": "#ff8153",
        "data": 90
      }, {
        "label": "SASS",
        "color": "#878bb6",
        "data": 75
      }, {
        "label": "Jade",
        "color": "#b2d767",
        "data": 120
      }];
      var options = {
        series: {
          pie: {
            show: true,
            innerRadius: 0,
            label: {
              show: true,
              radius: 0.8,
              formatter: function formatter(label, series) {
                return '<div class="flot-pie-label">' + //label + ' : ' +
                Math.round(series.percent) + '%</div>';
              },
              background: {
                opacity: 0.8,
                color: '#222'
              }
            }
          }
        }
      };
      var chart = $('.chart-pie');
      if (chart.length) $.plot(chart, data, options);
    }
  })();
  /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js")))
  
  /***/ }),
  
  /***/ 5:
  /*!*********************************************************!*\
    !*** multi ./resources/js/angle/modules/charts/flot.js ***!
    \*********************************************************/
  /*! no static exports found */
  /***/ (function(module, exports, __webpack_require__) {
  
  module.exports = __webpack_require__(/*! X:\proyect\wb\angle4\development\laravel\resources\js\angle\modules\charts\flot.js */"./resources/js/angle/modules/charts/flot.js");
  
  
  /***/ })
  
  },[[5,"/js/manifest","/js/vendor"]]]);