src/data-canvas.js
function ClickTrackingContext(ctx, px, py) {
  forward(this, ctx);

  var stack = [];
  this.hits = [];
  this.hit = null;
  
  var that = this;
  function recordHit() {
    that.hits.unshift(Array.prototype.slice.call(stack));
    that.hit = that.hits[0];
  }

  this.pushObject = function(o) {
    stack.unshift(o);
  };
  
  this.popObject = function() {
    stack.shift();
  };

  this.reset = function() {
    this.hits = [];
    this.hit = null;
  };

  // These are (most of) the canvas methods which draw something.
  // TODO: would it make sense to purge existing hits covered by this?
  this.clearRect = function(x, y, w, h) { };

  this.fillRect = function(x, y, w, h) {
    if (px >= x && px <= x + w && py >= y && py <= y + h) recordHit();
  };

  this.strokeRect = function(x, y, w, h) {
    // ...
  };

  this.fill = function(fillRule) {
    // TODO: implement fillRule
    if (ctx.isPointInPath(px, py)) recordHit();
  };

  this.stroke = function() {
    if (ctx.isPointInStroke(px, py)) recordHit();
  };

  this.fillText = function(text, x, y, maxWidth) {
    // ...
  };

  this.strokeText = function(text, x, y, maxWidth) {
    // ...
  };
}
function ClickTrackingContext(ctx, px, py) {
  forward(this, ctx);

  var stack = [];
  this.hits = [];
  this.hit = null;
  
  var that = this;
  function recordHit() {
    that.hits.unshift(Array.prototype.slice.call(stack));
    that.hit = that.hits[0];
  }

  this.pushObject = function(o) {
    stack.unshift(o);
  };
  
  this.popObject = function() {
    stack.shift();
  };

  this.reset = function() {
    this.hits = [];
    this.hit = null;
  };

  // These are (most of) the canvas methods which draw something.
  // TODO: would it make sense to purge existing hits covered by this?
  this.clearRect = function(x, y, w, h) { };

  this.fillRect = function(x, y, w, h) {
    if (px >= x && px <= x + w && py >= y && py <= y + h) recordHit();
  };

  this.strokeRect = function(x, y, w, h) {
    // ...
  };

  this.fill = function(fillRule) {
    // TODO: implement fillRule
    if (ctx.isPointInPath(px, py)) recordHit();
  };

  this.stroke = function() {
    if (ctx.isPointInStroke(px, py)) recordHit();
  };

  this.fillText = function(text, x, y, maxWidth) {
    // ...
  };

  this.strokeText = function(text, x, y, maxWidth) {
    // ...
  };
}
A context which determines the data at a particular location.

When drawing methods are called on this class, nothing is rendered. Instead,
each shape is checked to see if it includes the point of interest. If it
does, the current data stack is saved as a "hit".

The `hits` property records all such hits.
The `hit` property records only the last (top) hit.