<script type="text/javascript" charset="utf-8">
  /**
   * A reusable HTML Import to enable zooming on images.
   *
   * To use, simply include this HTML Import and add the class 'zoom' to any
   * images you want zoomable.
   *
   *     <link rel='import' type='text/html' href='/res/imp/zoom.html'>
   *
   *     <img src="http://..." class="zoom"/>
   *
   * Any number of images on a page can be zoomable.
   *
   * If you want to display the rgb colors of the pixel at the center of the
   * zoom then add an id of 'zoomHex' to any element that supports
   * textContent, such as a div, p, span, etc.
   *
   *     <p id=zoomHex></p>
   *
   * Note that HTML Imports need to be polyfilled in the near term.
   */
  (function () {
    function onLoad() {
      var PIXELS       = 20; // The number of pixels in width and height in a zoom.
      var clientX      = 0;
      var clientY      = 0;
      var lastClientX  = 0;
      var lastClientY  = 0;
      var ctx          = null; // The 2D canvas context of the zoom.
      var currentImage = null; // The img node we are zooming for, otherwise null.
      var hex          = document.getElementById('zoomHex');
      var canvasCopy   = null;
      function zoomMove(e) {
        clientX = e.clientX;
        clientY = e.clientY;
      }
      function zoomMouseDown(e) {
        e.preventDefault();
        // Only do zooming on the primary mouse button.
        if (e.button != 0) {
          return
        }
        currentImage = e.target;
        clientX = e.clientX;
        clientY = e.clientY;
        lastClientX = clientX-1;
        lastClientY = clientY-1;
        document.body.style.cursor = 'crosshair';
        canvas = document.createElement('canvas');
        canvas.width = 1024;
        canvas.height = 1024;
        canvas.classList.add('zoomCanvas');
        ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = false;
        this.parentNode.insertBefore(canvas, this);
        // Copy the image over to a canvas so we can read RGBA values for each point.
        if (hex) {
          canvasCopy = document.createElement('canvas');
          canvasCopy.width = currentImage.width;
          canvasCopy.height = currentImage.height;
          canvasCopy.id = 'zoomCopy';
          canvasCopy.getContext('2d').drawImage(currentImage, 0, 0, currentImage.width, currentImage.height);
          this.parentNode.insertBefore(canvasCopy, this);
        }
        document.body.addEventListener('pointermove',   zoomMove,     true);
        document.body.addEventListener('pointerup',     zoomFinished);
        document.body.addEventListener('pointerleave',  zoomFinished);
        // Kick off the drawing.
        setTimeout(drawZoom, 1);
      }
      function hexify(i) {
        var s = i.toString(16).toUpperCase();
        // Pad out to two hex digits if necessary.
        if (s.length < 2) {
          s = '0' + s;
        }
        return s;
      }
      function drawZoom() {
        if (currentImage) {
          // Only draw if the mouse has moved from the last time we drew.
          if (lastClientX != clientX || lastClientY != clientY) {
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
            var x = clientX - currentImage.x;
            var y = clientY - currentImage.y;
            var dx = Math.floor(ctx.canvas.width/PIXELS);
            var dy = Math.floor(ctx.canvas.height/PIXELS);
            ctx.lineWidth = 1;
            ctx.strokeStyle = '#000';
            // Draw out each pixel as a rect on the target canvas, as this works around
            // FireFox doing a blur as it copies from one canvas to another.
            var colors = canvasCopy.getContext('2d').getImageData(x, y, PIXELS, PIXELS).data;
            for (var i=0; i<PIXELS; i++) {
              for (var j=0; j<PIXELS; j++) {
                var offset = (j*PIXELS+i)*4; // Offset into the colors array.
                ctx.fillStyle = 'rgba(' + colors[offset] + ', ' + colors[offset+1] + ', ' + colors[offset+2] + ', ' + colors[offset+3]/255.0 + ')';
                ctx.fillRect(i*dx, j*dy, dx-1, dy-1);
                // Box and label one selected pixel with its rgba values.
                if (hex && i==PIXELS/2 && j == PIXELS/2) {
                  ctx.strokeRect(i*dx, j*dy, dx-1, dy-1);
                  hex.textContent = 'rgba('
                    + colors[offset] + ', '
                    + colors[offset+1] + ', '
                    + colors[offset+2] + ', '
                    + colors[offset+3] + ') '
                    + hexify(colors[offset])
                    + hexify(colors[offset+1])
                    + hexify(colors[offset+2])
                    + hexify(colors[offset+3]);
                }
              }
            }
            lastClientX = clientX;
            lastClientY = clientY;
          }
          setTimeout(drawZoom, 1000/30);
        }
      }
      function zoomFinished() {
        currentImage = null;
        if (hex) {
          hex.textContent = '';
        }
        document.body.style.cursor = 'default';
        ctx.canvas.parentNode.removeChild(ctx.canvas);
        canvasCopy.parentNode.removeChild(canvasCopy);
        document.body.removeEventListener('pointermove',  zoomMove,     true);
        document.body.removeEventListener('pointerup',    zoomFinished);
        document.body.removeEventListener('pointerleave', zoomFinished);
      }

      var zoomables = document.body.querySelectorAll('.zoom');
      for (var i=0; i<zoomables.length; i++) {
        zoomables[i].addEventListener('pointerdown', zoomMouseDown);
      }
    }

    // If loaded via HTML Imports then DOMContentLoaded will be long done.
    if (document.readyState != "loading") {
      onLoad();
    } else {
      this.addEventListener('DOMContentLoaded', onLoad);
    }
  })();
</script>

<style type="text/css" media="screen">
  .zoom {
    cursor: crosshair;
  }

  .zoomCanvas {
    position: absolute;
    width: vmin;
    height: vmin;
    top: 3em;
    right: 1em;
    z-index: -1;
  }

  #zoomCopy {
    display: none;
  }

  #zoomHex {
    text-shadow: 1px 1px #eee;
  }
</style>