html5 boiler plate
[scannr.git] / js / flotr2 / js / types / lines.js
blob:a/js/flotr2/js/types/lines.js -> blob:b/js/flotr2/js/types/lines.js
  /** Lines **/
  Flotr.addType('lines', {
  options: {
  show: false, // => setting to true will show lines, false will hide
  lineWidth: 2, // => line width in pixels
  fill: false, // => true to fill the area from the line to the x axis, false for (transparent) no fill
  fillBorder: false, // => draw a border around the fill
  fillColor: null, // => fill color
  fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
  steps: false, // => draw steps
  stacked: false // => setting to true will show stacked lines, false will show normal lines
  },
   
  stack : {
  values : []
  },
   
  /**
  * Draws lines series in the canvas element.
  * @param {Object} options
  */
  draw : function (options) {
   
  var
  context = options.context,
  lineWidth = options.lineWidth,
  shadowSize = options.shadowSize,
  offset;
   
  context.save();
  context.lineJoin = 'round';
   
  if (shadowSize) {
   
  context.lineWidth = shadowSize / 2;
  offset = lineWidth / 2 + context.lineWidth / 2;
   
  // @TODO do this instead with a linear gradient
  context.strokeStyle = "rgba(0,0,0,0.1)";
  this.plot(options, offset + shadowSize / 2, false);
   
  context.strokeStyle = "rgba(0,0,0,0.2)";
  this.plot(options, offset, false);
  }
   
  context.lineWidth = lineWidth;
  context.strokeStyle = options.color;
   
  this.plot(options, 0, true);
   
  context.restore();
  },
   
  plot : function (options, shadowOffset, incStack) {
   
  var
  context = options.context,
  width = options.width,
  height = options.height,
  xScale = options.xScale,
  yScale = options.yScale,
  data = options.data,
  stack = options.stacked ? this.stack : false,
  length = data.length - 1,
  prevx = null,
  prevy = null,
  zero = yScale(0),
  start = null,
  x1, x2, y1, y2, stack1, stack2, i;
   
  if (length < 1) return;
   
  context.beginPath();
   
  for (i = 0; i < length; ++i) {
   
  // To allow empty values
  if (data[i][1] === null || data[i+1][1] === null) {
  if (options.fill) {
  if (i > 0 && data[i][1]) {
  context.stroke();
  fill();
  start = null;
  context.closePath();
  context.beginPath();
  }
  }
  continue;
  }
   
  // Zero is infinity for log scales
  // TODO handle zero for logarithmic
  // if (xa.options.scaling === 'logarithmic' && (data[i][0] <= 0 || data[i+1][0] <= 0)) continue;
  // if (ya.options.scaling === 'logarithmic' && (data[i][1] <= 0 || data[i+1][1] <= 0)) continue;
   
  x1 = xScale(data[i][0]);
  x2 = xScale(data[i+1][0]);
   
  if (start === null) start = data[i];
   
  if (stack) {
   
  stack1 = stack.values[data[i][0]] || 0;
  stack2 = stack.values[data[i+1][0]] || stack.values[data[i][0]] || 0;
   
  y1 = yScale(data[i][1] + stack1);
  y2 = yScale(data[i+1][1] + stack2);
   
  if(incStack){
  stack.values[data[i][0]] = data[i][1]+stack1;
   
  if(i == length-1)
  stack.values[data[i+1][0]] = data[i+1][1]+stack2;
  }
  }
  else{
  y1 = yScale(data[i][1]);
  y2 = yScale(data[i+1][1]);
  }
   
  if (
  (y1 > height && y2 > height) ||
  (y1 < 0 && y2 < 0) ||
  (x1 < 0 && x2 < 0) ||
  (x1 > width && x2 > width)
  ) continue;
   
  if((prevx != x1) || (prevy != y1 + shadowOffset))
  context.moveTo(x1, y1 + shadowOffset);
   
  prevx = x2;
  prevy = y2 + shadowOffset;
  if (options.steps) {
  context.lineTo(prevx + shadowOffset / 2, y1 + shadowOffset);
  context.lineTo(prevx + shadowOffset / 2, prevy);
  } else {
  context.lineTo(prevx, prevy);
  }
  }
   
  if (!options.fill || options.fill && !options.fillBorder) context.stroke();
   
  fill();
   
  function fill () {
  // TODO stacked lines
  if(!shadowOffset && options.fill && start){
  x1 = xScale(start[0]);
  context.fillStyle = options.fillStyle;
  context.lineTo(x2, zero);
  context.lineTo(x1, zero);
  context.lineTo(x1, yScale(start[1]));
  context.fill();
  if (options.fillBorder) {
  context.stroke();
  }
  }
  }
   
  context.closePath();
  },
   
  // Perform any pre-render precalculations (this should be run on data first)
  // - Pie chart total for calculating measures
  // - Stacks for lines and bars
  // precalculate : function () {
  // }
  //
  //
  // Get any bounds after pre calculation (axis can fetch this if does not have explicit min/max)
  // getBounds : function () {
  // }
  // getMin : function () {
  // }
  // getMax : function () {
  // }
  //
  //
  // Padding around rendered elements
  // getPadding : function () {
  // }
   
  extendYRange : function (axis, data, options, lines) {
   
  var o = axis.options;
   
  // If stacked and auto-min
  if (options.stacked && ((!o.max && o.max !== 0) || (!o.min && o.min !== 0))) {
   
  var
  newmax = axis.max,
  newmin = axis.min,
  positiveSums = lines.positiveSums || {},
  negativeSums = lines.negativeSums || {},
  x, j;
   
  for (j = 0; j < data.length; j++) {
   
  x = data[j][0] + '';
   
  // Positive
  if (data[j][1] > 0) {
  positiveSums[x] = (positiveSums[x] || 0) + data[j][1];
  newmax = Math.max(newmax, positiveSums[x]);
  }
   
  // Negative
  else {
  negativeSums[x] = (negativeSums[x] || 0) + data[j][1];
  newmin = Math.min(newmin, negativeSums[x]);
  }
  }
   
  lines.negativeSums = negativeSums;
  lines.positiveSums = positiveSums;
   
  axis.max = newmax;
  axis.min = newmin;
  }
   
  if (options.steps) {
   
  this.hit = function (options) {
  var
  data = options.data,
  args = options.args,
  yScale = options.yScale,
  mouse = args[0],
  length = data.length,
  n = args[1],
  x = options.xInverse(mouse.relX),
  relY = mouse.relY,
  i;
   
  for (i = 0; i < length - 1; i++) {
  if (x >= data[i][0] && x <= data[i+1][0]) {
  if (Math.abs(yScale(data[i][1]) - relY) < 8) {
  n.x = data[i][0];
  n.y = data[i][1];
  n.index = i;
  n.seriesIndex = options.index;
  }
  break;
  }
  }
  };
   
  this.drawHit = function (options) {
  var
  context = options.context,
  args = options.args,
  data = options.data,
  xScale = options.xScale,
  index = args.index,
  x = xScale(args.x),
  y = options.yScale(args.y),
  x2;
   
  if (data.length - 1 > index) {
  x2 = options.xScale(data[index + 1][0]);
  context.save();
  context.strokeStyle = options.color;
  context.lineWidth = options.lineWidth;
  context.beginPath();
  context.moveTo(x, y);
  context.lineTo(x2, y);
  context.stroke();
  context.closePath();
  context.restore();
  }
  };
   
  this.clearHit = function (options) {
  var
  context = options.context,
  args = options.args,
  data = options.data,
  xScale = options.xScale,
  width = options.lineWidth,
  index = args.index,
  x = xScale(args.x),
  y = options.yScale(args.y),
  x2;
   
  if (data.length - 1 > index) {
  x2 = options.xScale(data[index + 1][0]);
  context.clearRect(x - width, y - width, x2 - x + 2 * width, 2 * width);
  }
  };
  }
  }
   
  });