Merge "veyron/examples/pipetobrowser: Pagination support for the DataGrid component"
diff --git a/examples/pipetobrowser/browser/libs/css/common-style.css b/examples/pipetobrowser/browser/libs/css/common-style.css
index 62bc38c..cc263ea 100644
--- a/examples/pipetobrowser/browser/libs/css/common-style.css
+++ b/examples/pipetobrowser/browser/libs/css/common-style.css
@@ -2,6 +2,10 @@
   display: none !important;
 }
 
+.invisible {
+  visibility: hidden;
+}
+
 .secondary-text {
   color: rgba(0,0,0,.54);
 }
diff --git a/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.css b/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.css
index f2186e2..87cdff3 100644
--- a/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.css
+++ b/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.css
@@ -83,4 +83,18 @@
 
 .more-dialog-content [moreInfoOnly] {
   display: initial;
+}
+
+.paginator {
+  display: inline-block;
+  border: solid 1px rgba(0, 0, 0, 0.05);
+  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
+  color: rgba(0, 0, 0, 0.54);
+  fill: rgba(0, 0, 0, 0.54);
+  margin: 1em;
+  font-size: 0.9em;
+}
+
+.paginator paper-icon-button {
+  vertical-align: middle;
 }
\ No newline at end of file
diff --git a/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.html b/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.html
index d9ed8f9..0824c37 100644
--- a/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.html
+++ b/examples/pipetobrowser/browser/libs/ui-components/data-grid/grid/component.html
@@ -8,13 +8,13 @@
 <link rel="import" href="/libs/ui-components/data-grid/grid/cell/renderer.html">
 <link rel="import" href="/libs/ui-components/data-grid/grid/column/renderer.html">
 
-<polymer-element name="p2b-grid" attributes="summary dataSource defaultSortKey defaultSortAscending">
+<polymer-element name="p2b-grid" attributes="summary dataSource defaultSortKey defaultSortAscending pageSize">
   <template>
     <link rel="stylesheet" href="/libs/css/common-style.css">
     <link rel="stylesheet" href="component.css">
     <div id="templates"></div>
     <core-collapse id="searchTools">
-      <div class="result-count">Showing {{ dataSourceResult.length }} items</div>
+      <div class="result-count">Showing {{ dataSourceResult.length }} items of {{totalNumItems}}</div>
       <div>
         <content select="[grid-search]"></content>
       </div>
@@ -43,13 +43,24 @@
             <template ref="{{ col.cellTemplateId }}" bind></template>
           </td>
           <td class="info-column">
-            <paper-icon-button on-tap="{{ showMoreInfo }}" class="more-icon" icon="more-vert" title="more info"></paper-icon-button
+            <paper-icon-button on-click="{{ showMoreInfo }}" class="more-icon" icon="more-vert" title="more info"></paper-icon-button
             >
           </td>
         </tr>
       </tbody>
     </table>
 
+    <!-- Pagination -->
+    <template if="{{totalNumPages > 1}}">
+      <div class="paginator">
+        <paper-icon-button title="Previous page" icon="hardware:keyboard-arrow-left"
+        class="{{ {invisible : pageNumber == 1} | tokenList }}" on-click="{{ previousPage }}"></paper-icon-button>
+        <span>Page {{ pageNumber }} of {{ totalNumPages }}</span>
+        <paper-icon-button title="Next page" icon="hardware:keyboard-arrow-right"
+        class="{{ {invisible : onLastPage } | tokenList }}" on-click="{{ nextPage }}"></paper-icon-button>
+      </div>
+    </template>
+
     <!-- Dialog that displays all columns and their values when more info icon activated -->
     <paper-dialog id="dialog" heading="Details" transition="paper-dialog-transition-bottom">
       <template id="moreInfoTemplate" bind>
@@ -144,6 +155,13 @@
        */
       defaultSortAscending: false,
 
+      /*
+       * Number if items displayed in each page.
+       * Defaults to 30
+       * @type {integer}
+       */
+      pageSize: 30,
+
       showMoreInfo: function(e) {
         var item = e.target.templateInstance.model.item;
         this.selectedItems = [item];
@@ -155,6 +173,7 @@
         // private property fields
         this.columns = [];
         this.shouldRefetchData = true;
+        this.pageNumber = 1;
         this.dataSource = null;
         this.cachedDataSourceResult = [];
         this.gridState = {
@@ -192,7 +211,7 @@
        * @private
        */
       dataSourceChanged: function() {
-        this.refresh();
+        this.refresh(true);
       },
 
       /*
@@ -206,8 +225,8 @@
         for (key in this.gridState) {
           var observer = new ObjectObserver(this.gridState[key])
           observer.open(function() {
-            // refresh the grid on any mutations
-            self.refresh();
+            // refresh the grid on any mutations and go back to page one
+            self.refresh(true);
           });
         }
       },
@@ -280,10 +299,15 @@
 
       /*
        * Refreshed the grid by fetching the data again and updating the UI in the next render tick
+       * @param {bool} goBackToPageOne Optional parameter indicating that grid should go back
+       * to page 1 after refresh. false by default
        */
-      refresh: function() {
+      refresh: function(goBackToPageOne) {
         var self = this;
         requestAnimationFrame(function() {
+          if (goBackToPageOne) {
+            self.pageNumber = 1;
+          }
           self.shouldRefetchData = true;
         });
       },
@@ -372,12 +396,27 @@
           return this.cachedDataSourceResult;
         }
 
+        // fetch the data
         this.cachedDataSourceResult = this.dataSource.fetch(
           this.gridState.search,
           this.gridState.sort,
           this.gridState.filters
         );
 
+        // page the data
+        this.totalNumItems = this.cachedDataSourceResult.length;
+        // if there less data than current page number, go back to page 1
+        if (this.totalNumItems < (this.pageNumber - 1) * this.pageSize) {
+          this.pageNumber = 1;
+        }
+        this.totalNumPages = Math.ceil(this.totalNumItems / this.pageSize);
+        this.onLastPage = this.totalNumPages == this.pageNumber;
+
+        // skip and take
+        var startIndex = (this.pageNumber - 1) * this.pageSize;
+        var endIndex = startIndex + this.pageSize;
+        this.cachedDataSourceResult = this.cachedDataSourceResult.slice(startIndex, endIndex);
+
         this.shouldRefetchData = false;
 
         return this.cachedDataSourceResult;
@@ -389,6 +428,20 @@
        */
       toggleSearchTools: function() {
         this.$.searchTools.toggle();
+      },
+
+      nextPage: function() {
+        if (!this.onLastPage) {
+          this.pageNumber++;
+          this.refresh();
+        }
+      },
+
+      previousPage: function() {
+        if (this.pageNumber > 1) {
+          this.pageNumber--;
+          this.refresh();
+        }
       }
     });
   </script>
diff --git a/examples/pipetobrowser/browser/services/pipe-to-browser-server.js b/examples/pipetobrowser/browser/services/pipe-to-browser-server.js
index 8b4f2e5..5f40da4 100644
--- a/examples/pipetobrowser/browser/services/pipe-to-browser-server.js
+++ b/examples/pipetobrowser/browser/services/pipe-to-browser-server.js
@@ -55,7 +55,7 @@
   var p2b = {
     pipe($suffix, $stream) {
       return new Promise(function(resolve, reject) {
-        //TODO(aghassemi) publish-issue remove /pipe/ from the suffix
+        //TODO(aghassemi) publish-issue remove /pipe from the suffix
         $suffix = $suffix.substr(5);
 
         log.debug('received pipe request for:', $suffix);