html5 boiler plate
[scannr.git] / js / flotr2 / js / plugins / selection.js
blob:a/js/flotr2/js/plugins/selection.js -> blob:b/js/flotr2/js/plugins/selection.js
  /**
  * Selection Handles Plugin
  *
  *
  * Options
  * show - True enables the handles plugin.
  * drag - Left and Right drag handles
  * scroll - Scrolling handle
  */
  (function () {
   
  function isLeftClick (e, type) {
  return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1));
  }
   
  function boundX(x, graph) {
  return Math.min(Math.max(0, x), graph.plotWidth - 1);
  }
   
  function boundY(y, graph) {
  return Math.min(Math.max(0, y), graph.plotHeight);
  }
   
  var
  D = Flotr.DOM,
  E = Flotr.EventAdapter,
  _ = Flotr._;
   
   
  Flotr.addPlugin('selection', {
   
  options: {
  pinchOnly: null, // Only select on pinch
  mode: null, // => one of null, 'x', 'y' or 'xy'
  color: '#B6D9FF', // => selection box color
  fps: 20 // => frames-per-second
  },
   
  callbacks: {
  'flotr:mouseup' : function (event) {
   
  var
  options = this.options.selection,
  selection = this.selection,
  pointer = this.getEventPosition(event);
   
  if (!options || !options.mode) return;
  if (selection.interval) clearInterval(selection.interval);
   
  if (this.multitouches) {
  selection.updateSelection();
  } else
  if (!options.pinchOnly) {
  selection.setSelectionPos(selection.selection.second, pointer);
  }
  selection.clearSelection();
   
  if(selection.selecting && selection.selectionIsSane()){
  selection.drawSelection();
  selection.fireSelectEvent();
  this.ignoreClick = true;
  }
  },
  'flotr:mousedown' : function (event) {
   
  var
  options = this.options.selection,
  selection = this.selection,
  pointer = this.getEventPosition(event);
   
  if (!options || !options.mode) return;
  if (!options.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;
  if (!options.pinchOnly) selection.setSelectionPos(selection.selection.first, pointer);
  if (selection.interval) clearInterval(selection.interval);
   
  this.lastMousePos.pageX = null;
  selection.selecting = false;
  selection.interval = setInterval(
  _.bind(selection.updateSelection, this),
  1000 / options.fps
  );
  },
  'flotr:destroy' : function (event) {
  clearInterval(this.selection.interval);
  }
  },
   
  // TODO This isn't used. Maybe it belongs in the draw area and fire select event methods?
  getArea: function() {
   
  var
  s = this.selection.selection,
  a = this.axes,
  first = s.first,
  second = s.second,
  x1, x2, y1, y2;
   
  x1 = a.x.p2d(s.first.x);
  x2 = a.x.p2d(s.second.x);
  y1 = a.y.p2d(s.first.y);
  y2 = a.y.p2d(s.second.y);
   
  return {
  x1 : Math.min(x1, x2),
  y1 : Math.min(y1, y2),
  x2 : Math.max(x1, x2),
  y2 : Math.max(y1, y2),
  xfirst : x1,
  xsecond : x2,
  yfirst : y1,
  ysecond : y2
  };
  },
   
  selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},
  prevSelection: null,
  interval: null,
   
  /**
  * Fires the 'flotr:select' event when the user made a selection.
  */
  fireSelectEvent: function(name){
  var
  area = this.selection.getArea();
  name = name || 'select';
  area.selection = this.selection.selection;
  E.fire(this.el, 'flotr:'+name, [area, this]);
  },
   
  /**
  * Allows the user the manually select an area.
  * @param {Object} area - Object with coordinates to select.
  */
  setSelection: function(area, preventEvent){
  var options = this.options,
  xa = this.axes.x,
  ya = this.axes.y,
  vertScale = ya.scale,
  hozScale = xa.scale,
  selX = options.selection.mode.indexOf('x') != -1,
  selY = options.selection.mode.indexOf('y') != -1,
  s = this.selection.selection;
   
  this.selection.clearSelection();
   
  s.first.y = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);
  s.second.y = boundY((selX && !selY) ? this.plotHeight - 1: (ya.max - area.y2) * vertScale, this);
  s.first.x = boundX((selY && !selX) ? 0 : (area.x1 - xa.min) * hozScale, this);
  s.second.x = boundX((selY && !selX) ? this.plotWidth : (area.x2 - xa.min) * hozScale, this);
   
  this.selection.drawSelection();
  if (!preventEvent)
  this.selection.fireSelectEvent();
  },
   
  /**
  * Calculates the position of the selection.
  * @param {Object} pos - Position object.
  * @param {Event} event - Event object.
  */
  setSelectionPos: function(pos, pointer) {
  var mode = this.options.selection.mode,
  selection = this.selection.selection;
   
  if(mode.indexOf('x') == -1) {
  pos.x = (pos == selection.first) ? 0 : this.plotWidth;
  }else{
  pos.x = boundX(pointer.relX, this);
  }
   
  if (mode.indexOf('y') == -1) {
  pos.y = (pos == selection.first) ? 0 : this.plotHeight - 1;
  }else{
  pos.y = boundY(pointer.relY, this);
  }
  },
  /**
  * Draws the selection box.
  */
  drawSelection: function() {
   
  this.selection.fireSelectEvent('selecting');
   
  var s = this.selection.selection,
  octx = this.octx,
  options = this.options,
  plotOffset = this.plotOffset,
  prevSelection = this.selection.prevSelection;
   
  if (prevSelection &&
  s.first.x == prevSelection.first.x &&
  s.first.y == prevSelection.first.y &&
  s.second.x == prevSelection.second.x &&
  s.second.y == prevSelection.second.y) {
  return;
  }
   
  octx.save();
  octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});
  octx.lineWidth = 1;
  octx.lineJoin = 'miter';
  octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});
   
  this.selection.prevSelection = {
  first: { x: s.first.x, y: s.first.y },
  second: { x: s.second.x, y: s.second.y }
  };
   
  var x = Math.min(s.first.x, s.second.x),
  y = Math.min(s.first.y, s.second.y),
  w = Math.abs(s.second.x - s.first.x),
  h = Math.abs(s.second.y - s.first.y);
   
  octx.fillRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
  octx.strokeRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
  octx.restore();
  },
   
  /**
  * Updates (draws) the selection box.
  */
  updateSelection: function(){
  if (!this.lastMousePos.pageX) return;
   
  this.selection.selecting = true;
   
  if (this.multitouches) {
  this.selection.setSelectionPos(this.selection.selection.first, this.getEventPosition(this.multitouches[0]));
  this.selection.setSelectionPos(this.selection.selection.second, this.getEventPosition(this.multitouches[1]));
  } else
  if (this.options.selection.pinchOnly) {
  return;
  } else {
  this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);
  }
   
  this.selection.clearSelection();
   
  if(this.selection.selectionIsSane()) {
  this.selection.drawSelection();
  }
  },
   
  /**
  * Removes the selection box from the overlay canvas.
  */
  clearSelection: function() {
  if (!this.selection.prevSelection) return;
   
  var prevSelection = this.selection.prevSelection,
  lw = 1,
  plotOffset = this.plotOffset,
  x = Math.min(prevSelection.first.x, prevSelection.second.x),
  y = Math.min(prevSelection.first.y, prevSelection.second.y),
  w = Math.abs(prevSelection.second.x - prevSelection.first.x),
  h = Math.abs(prevSelection.second.y - prevSelection.first.y);
   
  this.octx.clearRect(x + plotOffset.left - lw + 0.5,
  y + plotOffset.top - lw,
  w + 2 * lw + 0.5,
  h + 2 * lw + 0.5);
   
  this.selection.prevSelection = null;
  },
  /**
  * Determines whether or not the selection is sane and should be drawn.
  * @return {Boolean} - True when sane, false otherwise.
  */
  selectionIsSane: function(){
  var s = this.selection.selection;
  return Math.abs(s.second.x - s.first.x) >= 5 ||
  Math.abs(s.second.y - s.first.y) >= 5;
  }
   
  });
   
  })();