reader/android: initial web-veiw JS

A make task has been added to web/Makefile for generating the JS build to be
loaded into the Android web view for PDF rendering via PDF.js:

    make public/pdf-web-view.js

The source file lives in browser/pdf-web-view.js to access the build artifacts
from a normal browser visit http://127.0.0.1:8080/pdf-web-view.html after
running `make start`.

The state atom has been attached to the global window and can be accessed from
the developers console or via [Android's evaluateJavascript
API](http://goo.gl/JWUIaz):

    window.atom.href.set(<url to pdf file>)
    window.atom.pages.current.set(<page number>)

This CL is a first pass at trying to get the rendering working and has some
small issues (error handling, render optimizations, etc.) being tracked as TODOs
in #44.

Change-Id: If7b91453107aa3b5f898ccb66e067163b03eb4af
diff --git a/.gitignore b/.gitignore
index f398c60..ee53436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
 web/npm-debug.log
 web/public/bundle.*
 web/tmp
+web/public/pdf-web-view.js
 
 # VDL generated stubs should be ignored
 web/browser/vanadium/v.io/*
diff --git a/web/Makefile b/web/Makefile
index 39e4120..74ab93e 100644
--- a/web/Makefile
+++ b/web/Makefile
@@ -19,7 +19,7 @@
 cloudsync_port ?= 8000
 id ?= $(shell if test -e tmp/id; then cat tmp/id; else PATH=$(PATH) bin/uuid; fi)
 
-all: public/bundle.js node_modules
+all: public/bundle.js public/pdf-web-view.js
 	@true  # silences watch
 
 .DELETE_ON_ERROR:
@@ -42,6 +42,10 @@
 		--transform [ envify --ID $(id) ] \
 		$< 1> $@
 
+.DELETE_ON_ERROR:
+public/pdf-web-view.js: browser/pdf-web-view.js node_modules
+	browserify --debug $< 1> $@
+
 .PHONY:
 distclean: clean
 	@$(RM) -fr node_modules
@@ -59,9 +63,12 @@
 	@$(RM) -fr browser/vanadium/vdl
 
 .PHONY:
-lint: node_modules
+dependency-check:
 	@dependency-check package.json --entry browser/main.js
 	@dependency-check package.json --entry browser/main.js --unused --no-dev
+
+.PHONY:
+lint: node_modules
 	@jshint .
 
 .PHONY:
diff --git a/web/browser/pdf-web-view.js b/web/browser/pdf-web-view.js
new file mode 100644
index 0000000..8270076
--- /dev/null
+++ b/web/browser/pdf-web-view.js
@@ -0,0 +1,130 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var debug = require('debug')('pdf-viewer');
+var document = require('global/document');
+var domready = require('domready');
+var struct = require('observ-struct');
+var value = require('observ');
+var window = require('global/window');
+
+var atom = struct({
+  debug: value(true),
+  href: value(null),
+  pdf: struct({
+    document: value(null),
+    page: value(null)
+  }),
+  pages: struct({
+    current: value(1),
+    total: value(0),
+  }),
+  scale: value(1),
+  progress: value(0),
+});
+
+window.atom = atom;
+window.debug = require('debug');
+
+// When the state atom is updated to toggle debugging call the correct methods
+// on the debug module.
+atom.debug(function debugchange(value) {
+  // TODO(jasoncampbell): reload page so changes are picked up.
+  if (value) {
+    window.debug.enable('pdf-*');
+  } else {
+    window.debug.disable();
+  }
+});
+
+// Global cache of the canvas element.
+var canvas = null;
+
+domready(function ondomready() {
+  debug('domready');
+
+  // Initial DOM Node setup.
+  canvas = document.createElement('canvas');
+  canvas.setAttribute('class','pdf-canvas');
+  document.body.style.margin = '0px';
+  document.body.style.padding = '0px';
+  document.body.appendChild(canvas);
+
+  // Watch for changes on the atom.href value, when it updates load the PDF file
+  // located at that location.
+  // Trigger with: atom.href.set(value)
+  atom.href(function hrefchange(href) {
+    debug('loading pdf file: %s', href);
+    PDFJS
+      .getDocument(href, null, password, progress)
+      .then(setPDF, error);
+  });
+
+  // Watch for page number changes and asyncronosly load the page from PDF.js
+  // APIs.
+  // Trigger with: atom.pages.current.set(value)
+  atom.pages.current(function pagechange(current) {
+    var total = atom.pages.total();
+
+    // Skip invalid operations.
+    if (current === 0 || !atom.pdf.document() || current > total) {
+      return;
+    }
+
+    debug('loading page: %s of %s', current, total);
+
+    var pdf = atom.pdf.document();
+    var success = atom.pdf.page.set.bind(null);
+    pdf.getPage(current).then(success, error);
+  });
+
+  // Watch for changes on the PDF.js page object. When it is updated trigger a
+  // render.
+  // TODO(jasoncampbell): To prevent rendering errors with frequent state
+  // updates renders should be queued in a raf.
+  atom.pdf.page(function pagechange(page) {
+    debug('rendering page');
+    // TODO(jasoncampbell): Use state set scale instead of defaulting to 1.0.
+    var scale = window.innerWidth/page.getViewport(1.0).width;
+    var viewport = page.getViewport(scale);
+
+    canvas.height = viewport.height;
+    canvas.width = viewport.width;
+
+    page.render({
+      canvasContext: canvas.getContext('2d'),
+      viewport: viewport
+    }).promise.then(noop, error);
+  });
+});
+
+function setPDF(pdf) {
+  atom.pdf.document.set(pdf);
+  atom.pages.total.set(pdf.numPages);
+  atom.pages.current.set(1);
+}
+
+function progress(update) {
+  var float = (update.loaded/update.total) * 100;
+  var value = Math.floor(float);
+
+  // For some reason the update.loaded value above can be higher than the
+  // update.total value, in that case we can assume the progress is 100%.
+  if (value > 100) {
+    value = 100;
+  }
+
+  atom.progress.set(value);
+}
+
+function password() {
+  debug('password required');
+}
+
+// TODO(jasoncampbell): Add better error reporting and exception capturing.
+function error(err) {
+  debug('error: %s', err.stack);
+}
+
+function noop() {}
diff --git a/web/package.json b/web/package.json
index 094463c..b966425 100644
--- a/web/package.json
+++ b/web/package.json
@@ -37,6 +37,8 @@
     "global": "^4.3.0",
     "insert-css": "^0.2.0",
     "mercury": "^14.0.0",
+    "observ": "^0.2.0",
+    "observ-struct": "^6.0.0",
     "once": "^1.3.2",
     "path-to-regexp": "^1.2.1",
     "pump": "^1.0.1",
diff --git a/web/public/pdf-web-view.html b/web/public/pdf-web-view.html
new file mode 100644
index 0000000..701a420
--- /dev/null
+++ b/web/public/pdf-web-view.html
@@ -0,0 +1,2 @@
+<script type="text/javascript" src="./pdf.js"></script>
+<script type="text/javascript" src="./pdf-web-view.js"></script>
\ No newline at end of file