text: ﻿/* Javascript plotting library for jQuery, version 0.8.3.
  
   Copyright (c) 2007-2014 IOLA and Ole Laursen.
   Licensed under the MIT license.
  
   */
  (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);
  (function ($) {
      var hasOwnProperty = Object.prototype.hasOwnProperty;
      if (!$.fn.detach) {
          $.fn.detach = function () {
              return this.each(function () {
                  if (this.parentNode) {
                      this.parentNode.removeChild(this)
                  }
              })
          }
      }
      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 (!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");
          var devicePixelRatio = window.devicePixelRatio || 1, backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
          this.pixelRatio = devicePixelRatio / backingStoreRatio;
          this.resize(container.width(), container.height());
          this.textContainer = null;
          this.text = {};
          this._textCache = {}
      }
  
      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;
          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
          }
          context.restore();
          context.save();
          context.scale(pixelRatio, pixelRatio)
      };
      Canvas.prototype.clear = function () {
          this.context.clearRect(0, 0, this.width, this.height)
      };
      Canvas.prototype.render = function () {
          var cache = this._textCache;
          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()
              }
          }
      };
      Canvas.prototype.getTextLayer = function (classes) {
          var layer = this.text[classes];
          if (layer == null) {
              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
      };
      Canvas.prototype.getTextInfo = function (layer, text, font, angle, width) {
          var textStyle, layerCache, styleCache, info;
          text = "" + text;
          if (typeof font === "object") {
              textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family
          } else {
              textStyle = font
          }
          layerCache = this._textCache[layer];
          if (layerCache == null) {
              layerCache = this._textCache[layer] = {}
          }
          styleCache = layerCache[textStyle];
          if (styleCache == null) {
              styleCache = layerCache[textStyle] = {}
          }
          info = styleCache[text];
          if (info == null) {
              var 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
      };
      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;
          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
          }
          for (var i = 0, position; position = positions[i]; i++) {
              if (position.x == x && position.y == y) {
                  position.active = true;
                  return
              }
          }
          position = {
              active: true,
              rendered: false,
              element: positions.length ? info.element.clone() : info.element,
              x: x,
              y: y
          };
          positions.push(position);
          position.element.css({top: Math.round(y), left: Math.round(x), "text-align": halign})
      };
      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
                  }
              }
          }
      };
      function Plot(placeholder, data_, options_, plugins) {
          var series = [], options = {
              colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
              legend: {
                  show: true,
                  noColumns: 1,
                  labelFormatter: null,
                  labelBoxBorderColor: "#ccc",
                  container: null,
                  position: "ne",
                  margin: 5,
                  backgroundColor: null,
                  backgroundOpacity: .85,
                  sorted: null
              },
              xaxis: {
                  show: null,
                  position: "bottom",
                  mode: null,
                  font: null,
                  color: null,
                  tickColor: null,
                  transform: null,
                  inverseTransform: null,
                  min: null,
                  max: null,
                  autoscaleMargin: null,
                  ticks: null,
                  tickFormatter: null,
                  labelWidth: null,
                  labelHeight: null,
                  reserveSpace: null,
                  tickLength: null,
                  alignTicksWithAxis: null,
                  tickDecimals: null,
                  tickSize: null,
                  minTickSize: null
              },
              yaxis: {autoscaleMargin: .02, position: "left"},
              xaxes: [],
              yaxes: [],
              series: {
                  points: {show: false, radius: 3, lineWidth: 2, fill: true, fillColor: "#ffffff", symbol: "circle"},
                  lines: {lineWidth: 2, fill: false, fillColor: null, steps: false},
                  bars: {
                      show: false,
                      lineWidth: 2,
                      barWidth: 1,
                      fill: true,
                      fillColor: null,
                      align: "left",
                      horizontal: false,
                      zero: true
                  },
                  shadowSize: 3,
                  highlightColor: null
              },
              grid: {
                  show: true,
                  aboveData: false,
                  color: "#545454",
                  backgroundColor: null,
                  borderColor: null,
                  tickColor: null,
                  margin: 0,
                  labelMargin: 5,
                  axisMargin: 8,
                  borderWidth: 2,
                  minBorderMargin: null,
                  markings: null,
                  markingsColor: "#f4f4f4",
                  markingsLineWidth: 2,
                  clickable: false,
                  hoverable: false,
                  autoHighlight: true,
                  mouseActiveRadius: 10
              },
              interaction: {redrawOverlayInterval: 1e3 / 60},
              hooks: {}
          }, surface = null, overlay = null, eventHolder = null, 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;
          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)
          };
          plot.hooks = hooks;
          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() {
              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);
              if (opts && opts.colors) {
                  options.colors = opts.colors
              }
              if (options.xaxis.color == null)options.xaxis.color = $.color.parse(options.grid.color).scale("a", .22).toString();
              if (options.yaxis.color == null)options.yaxis.color = $.color.parse(options.grid.color).scale("a", .22).toString();
              if (options.xaxis.tickColor == null)options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color;
              if (options.yaxis.tickColor == null)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", .22).toString();
              var i, axisOptions, axisCount, fontSize = placeholder.css("font-size"), fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, fontDefaults = {
                  style: placeholder.css("font-style"),
                  size: Math.round(.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)
                      }
                  }
              }
              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";
                  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";
                  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;
              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];
              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;
                      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")a = a.n;
              if (typeof a != "number")a = 1;
              return a
          }
  
          function allAxes() {
              return $.grep(xaxes.concat(yaxes), function (a) {
                  return a
              })
          }
  
          function canvasToAxisCoords(pos) {
              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) {
              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,
                  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;
              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 (neededColors <= maxIndex) {
                  neededColors = maxIndex + 1
              }
              var c, colors = [], colorPool = options.colors, colorPoolSize = colorPool.length, variation = 0;
              for (i = 0; i < neededColors; i++) {
                  c = $.color.parse(colorPool[i % colorPoolSize] || "#666");
                  if (i % colorPoolSize == 0 && i) {
                      if (variation >= 0) {
                          if (variation < .5) {
                              variation = -variation - .2
                          } else variation = 0
                      } else variation = -variation
                  }
                  colors[i] = c.scale("rgb", 1 + variation)
              }
              var colori = 0, s;
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
                  if (s.color == null) {
                      s.color = colors[colori].toString();
                      ++colori
                  } else if (typeof s.color == "number")s.color = colors[s.color].toString();
                  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 (s.lines.zero == null) {
                      s.lines.zero = !!s.lines.fill
                  }
                  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) {
                  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])
              }
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
                  data = s.data;
                  format = s.datapoints.format;
                  if (!format) {
                      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
                          }
                      }
                      s.datapoints.format = format
                  }
                  if (s.datapoints.pointsize != null)continue;
                  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;
                                      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];
                                  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 {
                          if (insertSteps && k > 0 && points[k - ps] != null && points[k - ps] != points[k] && points[k - ps + 1] != points[k + 1]) {
                              for (m = 0; m < ps; ++m)points[k + ps + m] = points[k + m];
                              points[k + 1] = points[k - ps + 1];
                              k += ps
                          }
                      }
                  }
              }
              for (i = 0; i < series.length; ++i) {
                  s = series[i];
                  executeHooks(hooks.processDatapoints, [s, s.datapoints])
              }
              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) {
                      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() {
              placeholder.css("padding", 0).children().filter(function () {
                  return !$(this).hasClass("flot-overlay") && !$(this).hasClass("flot-base")
              }).remove();
              if (placeholder.css("position") == "static")placeholder.css("position", "relative");
              surface = new Canvas("flot-base", placeholder);
              overlay = new Canvas("flot-overlay", placeholder);
              ctx = surface.context;
              octx = overlay.context;
              eventHolder = $(overlay.element).unbind();
              var existing = placeholder.data("plot");
              if (existing) {
                  existing.shutdown();
                  overlay.clear()
              }
              placeholder.data("plot", plot)
          }
  
          function bindEvents() {
              if (options.grid.hoverable) {
                  eventHolder.mousemove(onMouseMove);
                  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) {
              function identity(x) {
                  return x
              }
  
              var s, m, t = axis.options.transform || identity, it = axis.options.inverseTransform;
              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))
              }
              if (t == identity)axis.p2c = function (p) {
                  return (p - m) * s
              }; else axis.p2c = function (p) {
                  return (t(p) - m) * s
              };
              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) {
              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;
              $.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
                      }
                  }
              });
              if (outermost) {
                  axisMargin = 0
              }
              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}
                  }
              }
              axis.position = pos;
              axis.tickLength = tickLength;
              axis.box.padding = padding;
              axis.innermost = innermost
          }
  
          function allocateAxisBoxSecondPhase(axis) {
              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() {
              var minMargin = options.grid.minBorderMargin, axis, i;
              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};
              $.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;
              for (var a in plotOffset) {
                  var margin = options.grid.margin || 0;
                  plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0
              }
              executeHooks(hooks.processOffset, [plotOffset]);
              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) {
                      setupTickGeneration(axis);
                      setTicks(axis);
                      snapRangeToTicks(axis, axis.ticks);
                      measureTickLabels(axis)
                  });
                  for (i = allocatedAxes.length - 1; i >= 0; --i)allocateAxisBoxFirstPhase(allocatedAxes[i]);
                  adjustLayoutForThingsStickingOut();
                  $.each(allocatedAxes, function (_, axis) {
                      allocateAxisBoxSecondPhase(axis)
                  })
              }
              plotWidth = surface.width - plotOffset.left - plotOffset.right;
              plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
              $.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) {
                  var widen = max == 0 ? 1 : .01;
                  if (opts.min == null)min -= widen;
                  if (opts.max == null || opts.min != null)max += widen
              } else {
                  var margin = opts.autoscaleMargin;
                  if (margin != null) {
                      if (opts.min == null) {
                          min -= delta * margin;
                          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;
              var noTicks;
              if (typeof opts.ticks == "number" && opts.ticks > 0)noTicks = opts.ticks; else noTicks = .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, size;
              if (norm < 1.5) {
                  size = 1
              } else if (norm < 3) {
                  size = 2;
                  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;
              if (opts.mode == "time" && !axis.tickGenerator) {
                  throw new Error("Time mode requires the flot.time plugin.")
              }
              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 (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) {
                      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) {
                          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
                      };
                      if (!axis.mode && opts.tickDecimals == null) {
                          var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), ts = axis.tickGenerator(axis);
                          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))ticks = oticks(axis); else ticks = oticks
              }
              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) {
                  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;
              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();
              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";
                      if (ranges[key]) {
                          from = ranges[key].from;
                          to = ranges[key].to;
                          break
                      }
                  }
              }
              if (!ranges[key]) {
                  axis = coord == "x" ? xaxes[0] : yaxes[0];
                  from = ranges[coord + "1"];
                  to = ranges[coord + "2"]
              }
              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);
              var markings = options.grid.markings;
              if (markings) {
                  if ($.isFunction(markings)) {
                      axes = plot.getAxes();
                      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");
                      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;
                      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
                      }
                      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 ? .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)
                      }
                  }
              }
              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;
                  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)
                  }
                  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) + .5
                          } else {
                              x = Math.floor(x) + .5
                          }
                      }
                      ctx.moveTo(x, y);
                      ctx.lineTo(x + xoff, y + yoff);
                      ctx.stroke()
                  }
                  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 || 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) + .5; else y = Math.floor(y) + .5
                      }
                      ctx.moveTo(x, y);
                      ctx.lineTo(x + xoff, y + yoff)
                  }
                  ctx.stroke()
              }
              if (bw) {
                  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;
                  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;
                      if (y1 <= y2 && y1 < axisy.min) {
                          if (y2 < axisy.min)continue;
                          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
                      }
                      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
                      }
                      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
                      }
                      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;
                  while (true) {
                      if (ps > 0 && i > points.length + ps)break;
                      i += ps;
                      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) {
                              segmentEnd = i;
                              ps = -ps;
                              ypos = 2;
                              continue
                          }
                          if (ps < 0 && i == segmentStart + ps) {
                              ctx.fill();
                              areaOpen = false;
                              ps = -ps;
                              ypos = 1;
                              i = segmentStart = segmentEnd + ps;
                              continue
                          }
                      }
                      if (x1 == null || x2 == null)continue;
                      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
                      }
                      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) {
                          ctx.beginPath();
                          ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
                          areaOpen = true
                      }
                      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
                      }
                      var x1old = x1, x2old = x2;
                      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
                      }
                      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 (x1 != x1old) {
                          ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1))
                      }
                      ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
                      ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
                      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;
              if (lw > 0 && sw > 0) {
                  ctx.lineWidth = sw;
                  ctx.strokeStyle = "rgba(0,0,0,0.1)";
                  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 (lw == 0)lw = 1e-4;
              if (lw > 0 && sw > 0) {
                  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;
              if (horizontal) {
                  drawBottom = drawRight = drawTop = true;
                  drawLeft = false;
                  left = b;
                  right = x;
                  top = y + barLeft;
                  bottom = y + barRight;
                  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;
                  if (top < bottom) {
                      tmp = top;
                      top = bottom;
                      bottom = tmp;
                      drawBottom = true;
                      drawTop = false
                  }
              }
              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);
              if (fillStyleCallback) {
                  c.fillStyle = fillStyleCallback(bottom, top);
                  c.fillRect(left, top, right - left, bottom - top)
              }
              if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
                  c.beginPath();
                  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);
              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 : .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;
              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})
                      }
                  }
              }
              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
                      })
                  }
              }
              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) {
                      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)
                  }
              }
          }
  
          var highlights = [], redrawTimeout = null;
  
          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), my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, maxy = maxDistance / axisy.scale;
                  ps = s.datapoints.pointsize;
                  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;
                          if (x - mx > maxx || x - mx < -maxx || y - my > maxy || y - my < -maxy)continue;
                          var dx = Math.abs(axisx.p2c(x) - mouseX), dy = Math.abs(axisy.p2c(y) - mouseY), dist = dx * dx + dy * dy;
                          if (dist < smallestDistance) {
                              smallestDistance = dist;
                              item = [i, j / ps]
                          }
                      }
                  }
                  if (s.bars.show && !item) {
                      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;
                          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
              })
          }
  
          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) {
                  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) {
                  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) {
                  drawOverlay();
                  return
              }
              if (!redrawTimeout)redrawTimeout = setTimeout(drawOverlay, t)
          }
  
          function drawOverlay() {
              redrawTimeout = null;
              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", .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", .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 {
                  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
              }
          }
      }
  
      $.plot = function (placeholder, data, options) {
          var plot = new Plot($(placeholder), data, options, $.plot.plugins);
          return plot
      };
      $.plot.version = "0.8.3";
      $.plot.plugins = [];
      $.fn.plot = function (data, options) {
          return this.each(function () {
              $.plot(this, data, options)
          })
      };
      function floorInBase(n, base) {
          return base * Math.floor(n / base)
      }
  })(jQuery);
  