Edit in JSFiddle

// new namespace
var tcg = {};

// main graph controller
tcg.graph = new Class({

  options: {
    x_grid_step: 20, // grid step in px on x axis
    y_grid_step: 40, // grid step in px on y axis
    x_factor: 1, // multiplier factor of each grid step on x axis (value = m * grid-steps)
    y_factor: 10, // multiplier value of each grid step on y axis (value = m * grid-steps)
    x0: 40, // x coordinate (from bottom left) in px of the axis origin
    y0: 40,// y coordinate (from bottom left) in px of the axis origin
    cursor_width: 12, // width of the cursor rectangle
    first_point: [0, 0], // first point values [x, y]
  },
  Implements: [Options],
  /*
   * There are here three coordinate systems:
   * - ccoord: the coordinates of the canvas, starting from top left
   * - coord: the coordinates defined by the drawed axis
   * - values: the real properties values
   *
   * So there are some conversion functions.
   * The drawed axis coordinates are always used.
   */
  initialize: function(canvas_id, options) {

    this.setOptions(options);

    this.canvas = $(canvas_id);
    this.initCanvas();

    // temp canvas used for previews
    this.initTempCanvas();

    // undo redo functionality
    this.setUndoRedo();

    // init some store useful arrays
    this.points = [];
    this.redo_canvas = [];
    this.undo_canvas = [];
    this.redo_points = [];
    this.undo_points = [];

    // add first point
    first_point_coord = this.valuesToCoords(this.options.first_point[0], this.options.first_point[1]);
    this.addPoint(first_point_coord.x, first_point_coord.y);

  },
  /*
   * Numeric values to axis coordinates
   */
  valuesToCoords: function(vx, vy) {
    var x = vx / this.options.x_factor * this.options.x_grid_step;
    var y = vy / this.options.y_factor * this.options.y_grid_step;

    return {x: x, y: y};
  },
  coordsToValues: function(x, y) {
    var vx = x / this.options.x_grid_step * this.options.x_factor;
    var vy = y / this.options.y_grid_step * this.options.y_factor;

    return {x: vx, y: vy};
  },
  /*
   * Axis coordinates to canvas coordinates
   */
  coordsToCcoords: function(x, y) {
    return {x: x + this.options.x0, y: this.y_max - this.options.y0 - y};
  },
  /*
   * Canvas coordinates to axis coordinates
   */
  ccoordsToCoords: function(x, y) {
    return {x: x - this.options.x0, y: this.y_max - this.options.y0 - y};
  },
  /*
   * Gets canvas dimensions, context and draws the grid
   */
  initCanvas: function() {

    // get coordinates
    this.canvas_coords = this.canvas.getCoordinates(document.body);

    this.x_max = this.canvas_coords.width;
    this.y_max = this.canvas_coords.height;

    //  get context
    this.ctx = this.canvas.getContext('2d');

    // draw grid
    this.drawGrid();
  },
  /*
   * Draws the grid
   */
  drawGrid: function() {
    // x axis grid
    this.ctx.beginPath();
    ['x', 'y'].each(function(axis) {
      var dyn = 0;
      while(dyn <= this[axis + '_max']) { // @todo check condition
        this.drawGridLine(dyn, axis);
        dyn += this.options[axis + '_grid_step'];
      }
    }.bind(this))
  },
  /*
   * Draws a line of the grid
   */
  drawGridLine: function(dyn, axis) {
    this.ctx.lineWidth = 1;
    this.ctx.strokeStyle = '#eee'.hexToRgb();
    this.ctx.beginPath();
    var x = axis === 'x' ? dyn : 0;
    var y = axis === 'y' ? dyn : 0;
    var c_coords = this.coordsToCcoords(x, y);
    this.ctx.moveTo(c_coords.x, c_coords.y);
    var x_f = axis === 'x' ? dyn : this.x_max;
    var y_f = axis === 'y' ? dyn : this.y_max;
    var cf_coords = this.coordsToCcoords(x_f, y_f);
    this.ctx.lineTo(cf_coords.x, cf_coords.y);
    this.ctx.closePath();
    this.ctx.stroke();

    var label = dyn / this.options[axis + '_grid_step'] * this.options[axis + '_factor'];

    this.ctx.fillText(label, axis === 'x' ? c_coords.x : c_coords.x - 30, axis === 'x' ? c_coords.y + 30 : c_coords.y);
  },
  /*
   * Creates the temp canvas and add events to it
   */
  initTempCanvas: function() {
    this.temp_canvas = new Element('canvas.tmp_graph').setProperties({
      width: this.x_max,
      height: this.y_max
    }).setStyles({
      position: 'absolute',
      left: this.canvas_coords.left + 'px',
      top: this.canvas_coords.top + 'px'
    }).inject(document.body);

    this.temp_ctx = this.temp_canvas.getContext('2d');

    this.temp_canvas.addEvent('mousemove', this.dispatchEvent.bind(this));
    this.temp_canvas.addEvent('mouseout', this.dispatchEvent.bind(this));
    this.temp_canvas.addEvent('click', this.dispatchEvent.bind(this));
  },
  // Creates the undo, redo buttons and sets their events
  setUndoRedo: function() {
    this.undo_button =    new Element('input', {type: 'button', value: 'undo'}).addEvent('click', function() { this.restoreState('undo') }.bind(this));
    this.redo_button =    new Element('input', {type: 'button', value: 'redo'}).addEvent('click', function() { this.restoreState('redo') }.bind(this));

    var container = new Element('p').adopt(this.undo_button, this.redo_button).inject(this.canvas.getParent());
  },
  // Adds the canvas coordinates and axis coordinates to the event object before calling the event handler
  dispatchEvent: function(evt) {
    evt._cx = evt.page.x - this.canvas_coords.left;
    evt._cy = evt.page.y - this.canvas_coords.top;
    evt_coords = this.ccoordsToCoords(evt._cx, evt._cy);
    evt._x = evt_coords.x;
    evt._y = evt_coords.y;

    this[evt.type](evt);
  },
  /*
   * Preview of the point, link with the last point and values coordinates
   */
  mousemove: function(evt) {

    // can't go back in time
    if(evt._x <= (this.last_point.x + this.options.x_grid_step/2)) {
      return null;
    }

    // grid x coordinate near to mouse pointer (x axis is discrete)
    var gx = Math.round(evt._x / this.options.x_grid_step) * this.options.x_grid_step;
    // y axis is continuous
    var gy = evt._y;
    // canvas coordinates
    var gcoords = this.coordsToCcoords(gx, gy);
    
    // top left point of the cursor
    var nx = gx - this.options.cursor_width/2;
    var ny = gy - this.options.cursor_width/2;

    // cleae the temp canvas
    this.temp_ctx.clearRect(0, 0, this.x_max, this.y_max);

    // draw a line between last inserted point and the mouse pointer
    this.temp_ctx.beginPath();
    var last_point_ccoords = this.coordsToCcoords(this.last_point.x, this.last_point.y);
    this.temp_ctx.moveTo(last_point_ccoords.x, last_point_ccoords.y);
    this.temp_ctx.lineTo(gcoords.x, gcoords.y);
    this.temp_ctx.strokeStyle = '#aaa'.hexToRgb();
    this.temp_ctx.stroke();

    // show the point values coordinates
    var values = this.coordsToValues(gx, gy);
    var label = '(' + values.x + ', ' + values.y + ')';
    this.temp_ctx.fillText(label, gcoords.x + 10, gcoords.y + 10);
    this.temp_ctx.fillStyle = '#aaa'.hexToRgb();

    // draw the cursor
    this.temp_ctx.fillRect(gcoords.x - this.options.cursor_width/2, gcoords.y - this.options.cursor_width/2, this.options.cursor_width, this.options.cursor_width);
  },
  /*
   * clear preview on mouseout
   */
  mouseout: function() {
    this.temp_ctx.clearRect(0, 0, this.x_max, this.y_max);
  },
  /*
   * save the point when clicking
   */
  click: function(evt) {

    // can't draw in the past
    if(evt._x <= (this.last_point.x + this.options.x_grid_step/2)) {
      return null;
    }

    // grid coordinates near to mouse pointer (discrete axis)
    var gx = Math.round(evt._x / this.options.x_grid_step) * this.options.x_grid_step;
    // free move on y axis (continuous axis)
    var gy = evt._y;

    // add the point to the class array
    this.addPoint(gx, gy);

  },
  addPoint: function(x, y) {

    // save the state for history
    this.saveHistory('undo');

    // draw the point over the real canvas and clear the temp canvas
    this.ctx.drawImage(this.temp_canvas, 0, 0);
    this.temp_ctx.clearRect(0, 0, this.x_max, this.y_max);

    var ccoords = this.coordsToCcoords(x, y);
    // I know is such a repetition, but this way the cursor painted over the real canvas has a different color from the one painted on the temp canvas
    this.ctx.fillRect(ccoords.x - this.options.cursor_width/2, ccoords.y - this.options.cursor_width/2, this.options.cursor_width, this.options.cursor_width);
    // add the point to the storing array
    this.points.push({x: x, y: y});
    // update the last point (used to draw the line to the mouse pointer)
    this.setLastPoint();
  },
  /*
   * I just prefer to have the last point in an instance member
   */
  setLastPoint: function() {
    this.last_point = this.points[this.points.length - 1];
  },
  /*
   * Saves the state for history
   */
  saveHistory: function(dir) {
    this[dir + '_canvas'].push(this.canvas.toDataURL("image/png"));
    // puff, I want to assign by value!
    this[dir + '_points'].push(this.points.slice(0));
  },
  // Restore a saved state
  restoreState: function(dir) {
    // no saved state? go away
    if(!this[dir + '_canvas'].length) {
      return null;
    }

    // it's necessary to save the present state in the other history direction
    this.saveHistory(dir === 'undo' ? 'redo' : 'undo');
    // again by value!
    this.points = this[dir + '_points'].pop().slice(0);
    // get the state to restore from the history array (the last inserted element)
    var restore_state = this[dir + '_canvas'].pop();
    // draw the state
    var img = new Element('img', {'src':restore_state});
    img.onload = function() {
      this.ctx.clearRect(0, 0, this.x_max, this.y_max);
      this.ctx.drawImage(img, 0, 0, this.x_max, this.y_max);
      // always update the last point babe!
      this.setLastPoint();
    }.bind(this)
  }

})
<h1>Canvas</h1>
<canvas id="graph" width="800" height="500"></canvas>
<script type="text/javascript">
  var mygraph = new tcg.graph('graph');
</script>
body {
  margin: 0;
  padding: 10px;
}