release/projects/reader: componentization and page control

Added a page control component that allows the user to scroll through
the pages of the PDF being displayed.  Also componentized the file
picker widget and removed it from the pdf component.  Finally, some
small changes made to the existing PDF component and widget to enable
display of different pages.

Change-Id: Ia2bb067ef770a607958f2f1b244659fe1d11ff95
diff --git a/browser/components/file-picker/index.js b/browser/components/file-picker/index.js
new file mode 100644
index 0000000..5d7aba3
--- /dev/null
+++ b/browser/components/file-picker/index.js
@@ -0,0 +1,8 @@
+// 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.
+
+module.exports = {
+  state: require('./state'),
+  render: require('./render')
+}
diff --git a/browser/components/file-picker/render.js b/browser/components/file-picker/render.js
new file mode 100644
index 0000000..fb97f3f
--- /dev/null
+++ b/browser/components/file-picker/render.js
@@ -0,0 +1,18 @@
+// 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 hg = require('mercury')
+var h = require('mercury').h
+var sendFile = require('../../events/send-file')
+
+module.exports = render
+
+function render(state, channels) {
+  return h('.file-picker', [
+    h('input', {
+      type: 'file',
+      'ev-event': sendFile(channels.set)
+    })
+  ])
+}
diff --git a/browser/components/file-picker/state.js b/browser/components/file-picker/state.js
new file mode 100644
index 0000000..b029199
--- /dev/null
+++ b/browser/components/file-picker/state.js
@@ -0,0 +1,11 @@
+// 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 hg = require('mercury')
+
+module.exports = state
+
+function state(options) {
+  return hg.state()
+}
diff --git a/browser/components/page-control/index.js b/browser/components/page-control/index.js
new file mode 100644
index 0000000..5d7aba3
--- /dev/null
+++ b/browser/components/page-control/index.js
@@ -0,0 +1,8 @@
+// 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.
+
+module.exports = {
+  state: require('./state'),
+  render: require('./render')
+}
diff --git a/browser/components/page-control/render.js b/browser/components/page-control/render.js
new file mode 100644
index 0000000..d087a14
--- /dev/null
+++ b/browser/components/page-control/render.js
@@ -0,0 +1,26 @@
+// 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 format = require('format')
+var hg = require('mercury')
+var h = require('mercury').h
+
+module.exports = render
+
+function render(state, channels) {
+  // page message
+  var pageNumMessage = format('Page %s of %s', state.pageNum, state.numPages)
+
+  return h('.pagecontrols', [
+    h('button.prev', {
+      disabled: (state.pageNum <= 1),
+      'ev-click': hg.send(channels.prev)
+    }, 'Prev'),
+    h('span.pagecount', pageNumMessage),
+    h('button.next', {
+      disabled: (state.pageNum >= state.numPages),
+      'ev-click': hg.send(channels.next)
+    }, 'Next')
+  ])
+}
diff --git a/browser/components/page-control/state.js b/browser/components/page-control/state.js
new file mode 100644
index 0000000..b0db57f
--- /dev/null
+++ b/browser/components/page-control/state.js
@@ -0,0 +1,36 @@
+// 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 hg = require('mercury')
+
+module.exports = state
+
+function state(options) {
+  var state = hg.state({
+    pageNum: hg.value(1),
+    numPages: hg.value(0),
+    channels: {
+      next: next,
+      prev: prev
+    }
+  })
+
+  return state
+}
+
+function next(state, data) {
+  if (state.numPages() <= state.pageNum()) {
+    return
+  }
+
+  state.pageNum.set(state.pageNum() + 1)
+}
+
+function prev(state, data) {
+  if (state.pageNum() <= 1) {
+    return
+  }
+
+  state.pageNum.set(state.pageNum() - 1)
+}
diff --git a/browser/components/pdf/render.js b/browser/components/pdf/render.js
index 38fa0e5..70b05b2 100644
--- a/browser/components/pdf/render.js
+++ b/browser/components/pdf/render.js
@@ -11,17 +11,6 @@
 
 function render(state, channels) {
   return h('.pdf', [
-    hg.partial(controls, state, channels),
-    PDFWidget(state.pdf)
-  ])
-}
-
-// TODO(jasoncampebll): split this out into it's own component
-function controls(state, channels) {
-  return h('.controls', [
-    h('input', {
-      type: 'file',
-      'ev-event': sendFile(channels.set)
-    })
+    PDFWidget(state.pdf, state.pageNum)
   ])
 }
diff --git a/browser/components/pdf/state.js b/browser/components/pdf/state.js
index af906fd..007b60e 100644
--- a/browser/components/pdf/state.js
+++ b/browser/components/pdf/state.js
@@ -9,6 +9,7 @@
 function state(options) {
   var state = hg.state({
     pdf: hg.value(null),
+    pageNum: hg.value(1),
     channels: {
       set: set
     }
@@ -60,6 +61,7 @@
 
   function success(pdf) {
     state.pdf.set(pdf)
+    state.pageNum.set(1)
   }
 
   function error(err) {
diff --git a/browser/index.js b/browser/index.js
index cf78c5c..72e9bf3 100644
--- a/browser/index.js
+++ b/browser/index.js
@@ -5,19 +5,46 @@
 var domready = require('domready')
 var hg = require('mercury')
 var h = require('mercury').h
+var filePicker = require('./components/file-picker')
+var pageControls = require('./components/page-control')
 var pdf = require('./components/pdf')
 
 domready(function ondomready() {
   // Top level state.
   var state = hg.state({
-    pdf: pdf.state()
+    pdf: pdf.state(),
+    pageControls: pageControls.state()
+  })
+
+  // Connect component states together
+  state.pdf.pdf(function (newPDF) {
+    if (newPDF != null) {
+      state.pageControls.numPages.set(newPDF.numPages)
+      state.pageControls.pageNum.set(1)
+    } else {
+      state.pageControls.numPages.set(0)
+    }
+  })
+
+  state.pageControls.pageNum(function (newPageNum) {
+    if (state.pdf.pdf != null) {
+      state.pdf.pageNum.set(newPageNum)
+    }
   })
 
   hg.app(document.body, state, render);
 })
 
 function render(state) {
-  return h('div', [
-    hg.partial(pdf.render, state.pdf, state.pdf.channels)
-  ])
+  if (state.pdf.pdf == null) {
+    return h('div', [
+      hg.partial(filePicker.render, state.pdf, state.pdf.channels)
+    ])
+  } else {
+    return h('div', [
+      // hg.partial(filePicker.render, state.pdf, state.pdf.channels),
+      hg.partial(pageControls.render, state.pageControls, state.pageControls.channels),
+      hg.partial(pdf.render, state.pdf, state.pdf.channels)
+    ])
+  }
 }
diff --git a/browser/widgets/pdf-widget.js b/browser/widgets/pdf-widget.js
index c9b284e..49a62b6 100644
--- a/browser/widgets/pdf-widget.js
+++ b/browser/widgets/pdf-widget.js
@@ -4,12 +4,13 @@
 module.exports = PDFWidget
 
 // TODO(jasoncampebll): add verification for pdf object
-function PDFWidget(pdf) {
+function PDFWidget(pdf, pageNum) {
   if (!(this instanceof PDFWidget)) {
-    return new PDFWidget(pdf);
+    return new PDFWidget(pdf, pageNum);
   }
 
   this.pdf = pdf
+  this.pageNum = pageNum
 }
 
 PDFWidget.prototype.type = 'Widget';
@@ -25,6 +26,7 @@
 PDFWidget.prototype.update = function update(previous, element) {
   var widget = this
   var pdf = widget.pdf
+  var pageNum = widget.pageNum
 
   if (!pdf) {
     return
@@ -32,7 +34,7 @@
 
   console.log('pdf', pdf)
 
-  pdf.getPage(1).then(function(page) {
+  pdf.getPage(pageNum).then(function(page) {
     var scale = 1.5
     var viewport = page.getViewport(scale)
     var context = element.getContext('2d')
diff --git a/package.json b/package.json
index 7cef729..4e76d93 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
     "domready": "^1.0.8",
     "extend": "^2.0.1",
     "mercury": "^14.0.0",
-    "xtend": "^4.0.0"
+    "xtend": "^4.0.0",
+    "format": "~0.2.1"
   }
 }