gui/html:  Now files/action browsing is complete

File browsing for now lists all files in the folder **without**
directories.  We probably would need to implement normal browsing at
some point, but it requires modification of FileState.

File restoring is coming soon. Stay tuned.

Change-Id: I273366e04164716bfb53e92585cdf02808e4d06a
diff --git a/gui/html/chronoshare-helpers.js b/gui/html/chronoshare-helpers.js
new file mode 100644
index 0000000..ed95925
--- /dev/null
+++ b/gui/html/chronoshare-helpers.js
@@ -0,0 +1,58 @@
+function number_format( number, decimals, dec_point, thousands_sep ) {
+    // http://kevin.vanzonneveld.net
+    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +     bugfix by: Michael White (http://crestidg.com)
+    // +     bugfix by: Benjamin Lupton
+    // +     bugfix by: Allan Jensen (http://www.winternet.no)
+    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
+    // *     example 1: number_format(1234.5678, 2, '.', '');
+    // *     returns 1: 1234.57
+
+    var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
+    var d = dec_point == undefined ? "," : dec_point;
+    var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
+    var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
+
+    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
+}
+
+function SegNumToFileSize (segNum) {
+    filesize = segNum * 1024;
+
+    if (filesize >= 1073741824) {
+	filesize = number_format(filesize / 1073741824, 2, '.', '') + ' Gb';
+    } else {
+	if (filesize >= 1048576) {
+     	    filesize = number_format(filesize / 1048576, 2, '.', '') + ' Mb';
+   	} else {
+	    if (filesize > 1024) {
+    		filesize = number_format(filesize / 1024, 0) + ' Kb';
+  	    } else {
+    		filesize = '< 1 Kb';
+	    };
+ 	};
+    };
+    return filesize;
+};
+
+/**
+ * @brief Convert binary data represented as non-escaped hex string to Uint8Array
+ * @param str String like ba0cb43e4b9639c114a0487d5faa7c70452533963fc8beb37d1b67c09a48a21d
+ *
+ * Note that if string length is odd, null will be returned
+ */
+StringHashToUint8Array = function (str) {
+    if (str.length % 2 != 0) {
+        return null;
+    }
+
+    var buf = new Uint8Array (str.length / 2);
+
+    for (var i = 0; i < str.length; i+=2) {
+        value = parseInt (str.substring (i, i+2), 16);
+        buf[i/2] = value;
+    }
+
+    return buf;
+};
diff --git a/gui/html/chronoshare-navigation.js b/gui/html/chronoshare-navigation.js
new file mode 100644
index 0000000..7dafd71
--- /dev/null
+++ b/gui/html/chronoshare-navigation.js
@@ -0,0 +1,70 @@
+var CHRONOSHARE;
+
+var PAGE; // no default page anymore (no reason to have)
+var PARAMS = [ ];
+var URIPARAMS = "";
+
+function nav_anchor (aurl) {
+    aurl = aurl.split('#');
+    if (aurl[1])
+    {
+        aurl_split = aurl[1].split ('&');
+        page = aurl_split[0];
+
+        vars = [ ];
+        for (var i = 1; i < aurl_split.length; i++)
+        {
+            hash = aurl_split[i].split('=');
+            vars.push(hash[0]);
+            // there is strange double-encoding problem...
+            vars[hash[0]] = decodeURIComponent (decodeURIComponent (hash[1]));
+        }
+
+        if (page != PAGE)
+        {
+            PAGE = page;
+            PARAMS = vars;
+            URIPARAMS = aurl[1];
+
+            if (CHRONOSHARE) {
+                CHRONOSHARE.run ();
+            }
+        }
+        else if (aurl[1] != URIPARAMS)
+        {
+            PARAMS = vars;
+            URIPARAMS = aurl[1];
+
+            if (CHRONOSHARE) {
+                CHRONOSHARE.run ();
+            }
+        }
+    }
+}
+
+$(document).ready (function () {
+    nav_anchor (window.location.href);
+
+    if (!PARAMS.user || !PARAMS.folder)
+    {
+        $("#error").html ("user and folder must be be specified in the URL");
+        $("#error").removeClass ("hidden");
+        return;
+    }
+    else {
+        // update in-page URLs
+        $(".needs-get-url").each (function (index,element) {
+            this.href += "&user="+encodeURIComponent (encodeURIComponent (PARAMS.user))
+                + "&folder="+encodeURIComponent (encodeURIComponent (PARAMS.folder));
+        });
+        $(".needs-get-url").removeClass ("needs-get-url");
+    }
+
+    CHRONOSHARE = new ChronoShare (PARAMS.user, PARAMS.folder);
+    CHRONOSHARE.run ();
+
+    $(window).on('hashchange', function() {
+        nav_anchor (window.location.href);
+    });
+});
+
diff --git a/gui/html/chronoshare.js b/gui/html/chronoshare.js
index ffd9724..bf8b4a1 100644
--- a/gui/html/chronoshare.js
+++ b/gui/html/chronoshare.js
@@ -1,94 +1,78 @@
-var CHRONOSHARE;
+$.Class ("ChronoShare", { },
+ {
+     init: function (username, foldername) {
+         $("#folder-name").text (foldername);
+         $("#user-name").text (username);
 
-var PAGE = "folderHistory";
-var PARAMS = [ ];
-var URIPARAMS = "";
+         this.username = new Name (username);
+         this.files = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("info").add ("files").add ("folder");
 
-function nav_anchor (aurl) {
-    aurl = aurl.split('#');
-    if (aurl[1])
-    {
-        aurl_split = aurl[1].split ('&');
-        page = aurl_split[0];
+         this.actions = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("info").add ("actions");
 
-        vars = [ ];
-        for (var i = 1; i < aurl_split.length; i++)
-        {
-            hash = aurl_split[i].split('=');
-            vars.push(hash[0]);
-            // there is strange double-encoding problem...
-            vars[hash[0]] = decodeURIComponent (decodeURIComponent (hash[1]));
-        }
+         this.restore = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("cmd").add ("restore").add ("file");
 
-        if (page != PAGE)
-        {
-            PAGE = page;
-            PARAMS = vars;
-            URIPARAMS = aurl[1];
+         this.ndn = new NDN ({host:"127.0.0.1"});
+     },
 
-            if (CHRONOSHARE) {
-                CHRONOSHARE.run ();
-            }
-        }
-        else if (aurl != URIPARAMS)
-        {
-            PARAMS = vars;
-            URIPARAMS = aurl[1];
+     run: function () {
+         console.log ("RUN page: " + PAGE);
+         $("#loader").fadeIn (500);
+         $("#error").addClass ("hidden");
 
-            if (CHRONOSHARE) {
-                CHRONOSHARE.run ();
-            }
-        }
-    }
-}
+         cmd = {};
+         if (PAGE == "fileList") {
+             cmd = this.info_files (PARAMS.item);
+         }
+         else if (PAGE == "folderHistory") {
+             cmd = this.info_actions ("folder", PARAMS.item);
+         }
+         else if (PAGE == "fileHistory") {
+             cmd = this.info_actions ("file", PARAMS.item);
+         }
 
-$(document).ready (function () {
-    nav_anchor (window.location.href);
+         if (cmd.request && cmd.callback) {
+             console.log (cmd.request.to_uri ());
+             this.ndn.expressInterest (cmd.request, cmd.callback);
+         }
+         else {
+             $("#loader").fadeOut (500); // ("hidden");
+             $("#content").empty ();
+             if (cmd.error) {
+                 $("#error").html (cmd.error);
+             }
+             else {
+                 $("#error").html ("Unknown error with " + PAGE);
+             }
+             $("#error").removeClass ("hidden");
+         }
+     },
 
-    if (!PARAMS.user || !PARAMS.folder)
-    {
-        $("#error").html ("user and folder must be be specified in the URL");
-        $("#error").removeClass ("hidden");
-        return;
-    }
-    else {
-        // update in-page URLs
-        $(".needs-get-url").each (function (index,element) {
-            this.href += "&user="+encodeURIComponent (encodeURIComponent (PARAMS.user))
-                + "&folder="+encodeURIComponent (encodeURIComponent (PARAMS.folder));
-        });
-        $(".needs-get-url").removeClass ("needs-get-url");
-    }
+     info_files: function(folder) {
+         request = new Name ().add (this.files)./*add (folder_in_question).*/addSegment (PARAMS.offset?PARAMS.offset:0);
+         return { request:request, callback: new FilesClosure (this) };
+     },
 
-    CHRONOSHARE = new ChronoShare (PARAMS.user, PARAMS.folder);
-    CHRONOSHARE.run ();
+     info_actions: function (type/*"file" or "folder"*/, fileOrFolder /*file or folder name*/) {
+         if (type=="file" && !fileOrFolder) {
+             return { error: "info_actions: fileOrFolder parameter is missing" };
+         }
 
-    $(window).on('hashchange', function() {
-        nav_anchor (window.location.href);
-    });
-});
+         request = new Name ().add (this.actions).add (type);
+         if (fileOrFolder) {
+             request.add (fileOrFolder);
+         }
+         request.addSegment (PARAMS.offset?PARAMS.offset:0);
+         return { request: request, callback: new HistoryClosure (this) };
+     },
 
-/**
- * @brief Convert binary data represented as non-escaped hex string to Uint8Array
- * @param str String like ba0cb43e4b9639c114a0487d5faa7c70452533963fc8beb37d1b67c09a48a21d
- *
- * Note that if string length is odd, null will be returned
- */
-StringHashToUint8Array = function (str) {
-    if (str.length % 2 != 0) {
-        return null;
-    }
-
-    var buf = new Uint8Array (str.length / 2);
-
-    for (var i = 0; i < str.length; i+=2) {
-        value = parseInt (str.substring (i, i+2), 16);
-        buf[i/2] = value;
-    }
-
-    return buf;
-};
-
+     cmd_restore_file: function (filename, version, hash) {
+         request = new Name ().add (this.restore)
+             .add (filename)
+             .addSegment (version)
+             .add (hash);
+         console.log (request.to_uri ());
+     }
+ });
 
 $.Class ("FilesClosure", {}, {
     init: function (chronoshare) {
@@ -105,21 +89,24 @@
 	    }
             data = JSON.parse (convertedData);
 
+            tbody = $("<tbody />", { "id": "file-list-files" });
+
             // error handling?
-            table = $("#content").append (
+            newcontent = $("<div />", { "id": "content" }).append (
                 $("<table />", { "class": "item-list" })
                     .append ($("<thead />")
                              .append ($("<tr />")
                                       .append ($("<th />", { "class": "filename border-left", "scope": "col" }).text ("Filename"))
                                       .append ($("<th />", { "class": "version", "scope": "col" }).text ("Version"))
+                                      .append ($("<th />", { "class": "size", "scope": "col" }).text ("Size"))
                                       .append ($("<th />", { "class": "modified", "scope": "col" }).text ("Modified"))
                                       .append ($("<th />", { "class": "modified-by border-right", "scope": "col" }).text ("Modified By"))))
-                    .append ($("<tbody />", { "id": "file-list-files" }))
+                    .append (tbody)
                     .append ($("<tfoot />")
                              .append ($("<tr />")
-                                      .append ($("<td />", { "colspan": "4", "class": "border-right border-left" })))));
+                                      .append ($("<td />", { "colspan": "5", "class": "border-right border-left" })))));
+            newcontent.hide ();
 
-            var html = $("#file-list-files");
             for (var i = 0; i < data.files.length; i++) {
                 file = data.files[i];
 
@@ -130,28 +117,71 @@
                     $(this).toggleClass('highlighted');
                 });
 
-                row.attr ("filename", encodeURIComponent(encodeURIComponent(file.filename)));
+                row.attr ("filename", file.filename); //encodeURIComponent(encodeURIComponent(file.filename)));
+
 
                 row.bind('click', function (e) {
-                    url = "#fileHistory";
-                    url += "&item=" + $(this).attr ("filename");
-                    pos = URIPARAMS.indexOf ("&");
-                    if (pos >= 0) {
-                        url += URIPARAMS.substring (pos)
-                    }
+                    url = new HistoryClosure (null).base_url ("fileHistory")
+                    url += "&item=" + encodeURIComponent (encodeURIComponent ($(this).attr ("filename")));
 
                     document.location = url;
                 });
 
-		row.append ($("<td />", {"class": "border-left"}).text (file.filename));
-		row.append ($("<td />").text (file.version));
-		row.append ($("<td />").text (new Date (file.timestamp)));
-		row.append ($("<td />", {"class": "border-right"})
+		row.append ($("<td />", { "class": "filename border-left" }).text (file.filename));
+		row.append ($("<td />", { "class": "version" }).text (file.version));
+		row.append ($("<td />", { "class": "size" }).text (SegNumToFileSize (file.segNum)));
+		row.append ($("<td />", { "class": "modified" }).text (new Date (file.timestamp)));
+		row.append ($("<td />", { "class": "modified-by border-right"})
 			    .append ($("<userName />").text (file.owner.userName))
 			    .append ($("<seqNo> /").text (file.owner.seqNo)));
 
-		html = html.append (row);
+		tbody = tbody.append (row);
             }
+
+            // if (!PARAMS.offset || PARAMS.offset==0)
+            // {
+            $("#content").fadeOut ("fast", function () {
+                $(this).replaceWith (newcontent);
+                $("#content").fadeIn ("fast");
+            });
+
+            self = this; // small "cheat"
+            $("#content-nav").fadeOut ("fast", function () {
+                $("#content-nav a").hide ();
+
+                if (PARAMS.offset !== undefined || data.more !== undefined) {
+                    $("#content-nav").fadeIn ("fast");
+
+                    if (data.more !== undefined) {
+                        $("#get-more").show ();
+
+                        $("#get-more").unbind ('click').click (function () {
+                            url = self.base_url ();
+                            url += "&offset="+data.more;
+
+                            document.location = url;
+                        });
+                    }
+                    if (PARAMS.offset > 0) {
+                        $("#get-less").show ();
+
+                        $("#get-less").unbind ('click').click (function () {
+                            url = self.base_url ();
+                            if (PARAMS.offset > 1) {
+                                url += "&offset="+(PARAMS.offset - 1);
+                            }
+
+                            document.location = url;
+                        });
+                    }
+                }
+            });
+            // }
+            // else {
+            //     tbody.children ().each (function (index, row) {
+            //         $("#history-list-actions").append (row);
+            //     });
+            // }
         }
         else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
             $("#error").html ("Interest timed out");
@@ -161,6 +191,16 @@
             $("#error").html ("Unknown error happened");
             $("#error").removeClass ("hidden");
         }
+    },
+
+    base_url: function () {
+        url = "#fileList"+
+            "&user="+encodeURIComponent (encodeURIComponent (PARAMS.user)) +
+            "&folder="+encodeURIComponent (encodeURIComponent (PARAMS.folder));
+        if (PARAMS.item !== undefined) {
+            url += "&item="+encodeURIComponent (encodeURIComponent (PARAMS.item));
+        }
+        return url;
     }
 });
 
@@ -180,8 +220,9 @@
 	    }
             data = JSON.parse (convertedData);
 
-            // error handling?
-            table = $("#content").append (
+            tbody = $("<tbody />", { "id": "history-list-actions" });
+
+            newcontent = $("<div />", { "id": "content" }).append (
                 $("<table />", { "class": "item-list" })
                     .append ($("<thead />")
                              .append ($("<tr />")
@@ -189,44 +230,90 @@
                                       .append ($("<th />", { "class": "version", "scope": "col" }).text ("Version"))
                                       .append ($("<th />", { "class": "modified", "scope": "col" }).text ("Modified"))
                                       .append ($("<th />", { "class": "modified-by border-right", "scope": "col" }).text ("Modified By"))))
-                    .append ($("<tbody />", { "id": "history-list-actions" }))
+                    .append (tbody)
                     .append ($("<tfoot />")
                              .append ($("<tr />")
                                       .append ($("<td />", { "colspan": "4", "class": "border-right border-left" })))));
 
-            var html = $("#history-list-actions");
             for (var i = 0; i < data.actions.length; i++) {
                 action = data.actions[i];
 
 	        row = $("<tr />");
 	        if (i%2) { row.addClass ("odd"); }
+                if (action.action=="DELETE") { row.addClass ("delete"); }
 
                 row.bind('mouseenter mouseleave', function() {
                     $(this).toggleClass('highlighted');
                 });
 
-                row.attr ("filename", encodeURIComponent(encodeURIComponent(action.filename)));
+                // row.attr ("filename", );//encodeURIComponent(encodeURIComponent(action.filename)));
+                // row.attr ("version",
 
-                row.bind('click', function (e) {
-                    url = "#fileHistory";
-                    url += "&item=" + $(this).attr ("filename");
-                    pos = URIPARAMS.indexOf ("&");
-                    if (pos >= 0) {
-                        url += URIPARAMS.substring (pos)
-                    }
+                // row.bind('click', function (e) {
+                //     url = "#fileHistory";
+                //     url += "&item=" + $(this).attr ("filename");
+                //     pos = URIPARAMS.indexOf ("&");
+                //     if (pos >= 0) {
+                //         url += URIPARAMS.substring (pos)
+                //     }
 
-                    document.location = url;
-                });
+                //     document.location = url;
+                // });
 
-	        row.append ($("<td />", {"class": "border-left"}).text (action.filename));
-	        row.append ($("<td />").text (action.version));
-	        row.append ($("<td />").text (new Date (action.timestamp)));
-	        row.append ($("<td />")
+	        row.append ($("<td />", { "class": "filename border-left" }).text (action.filename));
+	        row.append ($("<td />", { "class": "version" }).text (action.version));
+	        row.append ($("<td />", { "class": "timestamp" }).text (new Date (action.timestamp)));
+	        row.append ($("<td />", { "class": "modified-by border-right" })
 	        	    .append ($("<userName />").text (action.id.userName))
 	        	    .append ($("<seqNo> /").text (action.id.seqNo)));
 
-	        html = html.append (row);
+	        tbody = tbody.append (row);
             }
+
+            // if (!PARAMS.offset || PARAMS.offset==0)
+            // {
+            $("#content").fadeOut ("fast", function () {
+                $(this).replaceWith (newcontent);
+                $("#content").fadeIn ("fast");
+            });
+
+            self = this; // small "cheat"
+            $("#content-nav").fadeOut ("fast", function () {
+                $("#content-nav a").hide ();
+
+                if (PARAMS.offset !== undefined || data.more !== undefined) {
+                    $("#content-nav").fadeIn ("fast");
+
+                    if (data.more !== undefined) {
+                        $("#get-more").show ();
+
+                        $("#get-more").unbind ('click').click (function () {
+                            url = self.base_url (PAGE);
+                            url += "&offset="+data.more;
+
+                            document.location = url;
+                        });
+                    }
+                    if (PARAMS.offset > 0) {
+                        $("#get-less").show ();
+
+                        $("#get-less").unbind ('click').click (function () {
+                            url = self.base_url (PAGE);
+                            if (PARAMS.offset > 1) {
+                                url += "&offset="+(PARAMS.offset - 1);
+                            }
+
+                            document.location = url;
+                        });
+                    }
+                }
+            });
+            // }
+            // else {
+            //     tbody.children ().each (function (index, row) {
+            //         $("#history-list-actions").append (row);
+            //     });
+            // }
         }
         else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
             $("#error").html ("Interest timed out");
@@ -236,52 +323,19 @@
             $("#error").html ("Unknown error happened");
             $("#error").removeClass ("hidden");
         }
+    },
+
+    base_url: function (page) {
+        url = "#"+page+
+            "&user="+encodeURIComponent (encodeURIComponent (PARAMS.user)) +
+            "&folder="+encodeURIComponent (encodeURIComponent (PARAMS.folder));
+        if (PARAMS.item !== undefined) {
+            url += "&item="+encodeURIComponent (encodeURIComponent (PARAMS.item));
+        }
+        return url;
     }
 });
 
 
-$.Class ("ChronoShare", { },
- {
-     init: function (username, foldername) {
-         this.username = new Name (username);
-         this.files = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("info").add ("files").add ("folder");
-
-         this.actions = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("info").add ("actions");
-
-         this.restore = new Name ("/localhost").add (this.username).add ("chronoshare").add (foldername).add ("cmd").add ("restore").add ("file");
-
-         // this.ndn = new NDN ({host:"127.0.0.1", getHostAndPort: function() { return {host: "127.0.0.1", port: 9696}}});
-         this.ndn = new NDN ({host:"127.0.0.1"});
-     },
-
-
-     run: function () {
-         $("#content").empty ();
-         $("#loader").fadeIn (500);
-         $("#error").addClass ("hidden");
-
-         if (PAGE == "fileList") {
-             request = new Name ().add (this.files)./*add (folder_in_question).*/add ("nonce").addSegment (0);
-             console.log (request.to_uri ());
-             this.ndn.expressInterest (request, new FilesClosure (this));
-         }
-         else if (PAGE == "folderHistory") {
-             request = new Name ().add (this.actions).add ("folder")./*add (folder_in_question).*/add ("nonce").addSegment (0);
-             console.log (request.to_uri ());
-             this.ndn.expressInterest (request, new HistoryClosure (this));
-         }
-         else if (PAGE == "fileHistory") {
-             if (!PARAMS.item) {
-                 $("#loader").fadeOut (500); // ("hidden");
-                 $("#error").html ("incorrect input for fileHistory command");
-                 $("#error").removeClass ("hidden");
-                 return;
-             }
-             request = new Name ().add (this.actions).add ("file").add (PARAMS.item).add ("nonce").addSegment (0);
-             console.log (request.to_uri ());
-             this.ndn.expressInterest (request, new HistoryClosure (this));
-         }
-     }
- });
 
 
diff --git a/gui/html/index.html b/gui/html/index.html
index 24de8ca..ffa46a0 100644
--- a/gui/html/index.html
+++ b/gui/html/index.html
@@ -17,22 +17,29 @@
 
   <body>
     <header>
-      <h1>ChronoShare</h1>
+      <h1>ChronoShare <green id="folder-name"></green> for <grey id="user-name"></grey></h1>
       <h2 id="folderName"></h2>
 
       <nav>
         <ul>
-          <li><a class="needs-get-url" href="#fileList">File list</a></li>
-          <li><a class="needs-get-url" href="#folderHistory">Folder history</a></li>
+          <li><a class="needs-get-url" href="#fileList">All files</a></li>
+          <li><a class="needs-get-url" href="#folderHistory">Recent actions</a></li>
         </ul>
       </nav>
     </header>
 
     <article>
-      <!-- <img id="loader" src="load.gif" /> -->
       <div id="content" class="hidden">
       </div>
 
+      <content-nav id="content-nav" style="display: none">
+        <ul>
+          <li><a id="get-less" class="ajax-action">&lt;&lt; Previous</a></li>
+          <li><a id="get-more" class="ajax-action">Next &gt;&gt;</a></li>
+        </ul>
+
+      </content-nav>
+
       <div class="hidden" id="loader">
         <img src="load.gif" />
       </div>
@@ -57,6 +64,8 @@
     </footer>
 
     <script src="detect.js"></script>
+    <script type="text/javascript" src="chronoshare-helpers.js"></script>
+    <script type="text/javascript" src="chronoshare-navigation.js"></script>
     <script type="text/javascript" src="chronoshare.js"></script>
     <script type="text/javascript">
       $(window).ready (function () {
diff --git a/gui/html/style.css b/gui/html/style.css
index cd16f8c..cc3face 100644
--- a/gui/html/style.css
+++ b/gui/html/style.css
@@ -28,8 +28,9 @@
 }

 

 article {

-    margin: 20px auto 0;

+    margin: 20px auto 20px;

     width: 90%;

+    padding-bottom: 20px;

 }

 

 footer {

@@ -64,9 +65,7 @@
     min-width: 587px;

     width: 100%;

     height: 40px;

-}

 

-nav {

     background-color: #EAF4EF;

     -moz-border-radius: 6px;

     border-radius: 6px;

@@ -189,9 +188,22 @@
 .filename {

     width: 50%;

 }

+

+.highlighted .filename {

+    font-weight: bold;

+}

+

+.delete .filename {

+    color: red;

+}

+

 .version {

     width: 5%;

 }

+.size {

+    width: 5%;

+    whitespace: nowrap;

+}

 .modified {

     width: 20%;

 }

@@ -227,4 +239,46 @@
 seqNo {

     display: inline-block;

     margin-left: 5px;

-}
\ No newline at end of file
+}

+

+.ajax-action {

+    cursor: pointer;

+}

+

+

+

+/* Navigation menu */

+content-nav {

+    display: inline-block;

+    margin: 0;

+    /* width: 100%; */

+    height: 24px;

+

+    background-color: #EAF4EF;

+    -moz-border-radius: 6px;

+    border-radius: 6px;

+    border: 1px solid #99CCB2;

+}

+

+content-nav ul {

+    margin: 0;

+    padding: 0px;

+}

+

+content-nav li {

+    list-style-type: none;

+    display: inline;

+    margin: 0;

+}

+

+content-nav li a {

+    display: inline-block;

+    color: #727272;

+    font-size: 14px;

+    line-height: 14px;

+    font-family: sans-serif;

+    text-decoration: none;

+    padding: 5px 30px 5px 30px;

+}

+

+content-nav li a.active, content-nav a:hover { color: #2D9A65; }

diff --git a/src/action-log.cc b/src/action-log.cc
index 71ee782..0a05dd2 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -701,9 +701,10 @@
       action.set_action      (static_cast<ActionItem_ActionType> (sqlite3_column_int   (stmt, 2)));
       action.set_filename    (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 3)), sqlite3_column_bytes (stmt, 3));
       std::string directory  (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 4)), sqlite3_column_bytes (stmt, 4));
+      action.set_version     (sqlite3_column_int64 (stmt, 5));
+
       if (action.action () == 0)
         {
-          action.set_version     (sqlite3_column_int64 (stmt, 5));
           action.set_timestamp   (sqlite3_column_int64 (stmt, 6));
           action.set_file_hash   (sqlite3_column_blob  (stmt, 7), sqlite3_column_bytes (stmt, 7));
           action.set_mtime       (sqlite3_column_int   (stmt, 8));
@@ -772,9 +773,10 @@
       action.set_action      (static_cast<ActionItem_ActionType> (sqlite3_column_int   (stmt, 2)));
       action.set_filename    (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 3)), sqlite3_column_bytes (stmt, 3));
       std::string directory  (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 4)), sqlite3_column_bytes (stmt, 4));
+      action.set_version     (sqlite3_column_int64 (stmt, 5));
+
       if (action.action () == 0)
         {
-          action.set_version     (sqlite3_column_int64 (stmt, 5));
           action.set_timestamp   (sqlite3_column_int64 (stmt, 6));
           action.set_file_hash   (sqlite3_column_blob  (stmt, 7), sqlite3_column_bytes (stmt, 7));
           action.set_mtime       (sqlite3_column_int   (stmt, 8));
diff --git a/src/state-server.cc b/src/state-server.cc
index 43f15e3..89b6095 100644
--- a/src/state-server.cc
+++ b/src/state-server.cc
@@ -77,11 +77,11 @@
   // currently supporting limited number of command.
   // will be extended to support all planned commands later
 
-  // <PREFIX_INFO>/"actions"/"all"/<nonce>/<segment>  get list of all actions
+  // <PREFIX_INFO>/"actions"/"all"/<segment>  get list of all actions
   m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("actions")("folder"), bind(&StateServer::info_actions_folder, this, _1));
   m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("actions")("file"),   bind(&StateServer::info_actions_file, this, _1));
 
-  // <PREFIX_INFO>/"filestate"/"all"/<nonce>/<segment>
+  // <PREFIX_INFO>/"filestate"/"all"/<segment>
   m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("files")("folder"), bind(&StateServer::info_files_folder, this, _1));
 
   // <PREFIX_CMD>/"restore"/"file"/<one-component-relative-file-name>/<version>/<file-hash>
@@ -174,8 +174,8 @@
 void
 StateServer::info_actions_folder (const Name &interest)
 {
-  if (interest.size () - m_PREFIX_INFO.size () != 4 &&
-      interest.size () - m_PREFIX_INFO.size () != 5)
+  if (interest.size () - m_PREFIX_INFO.size () != 3 &&
+      interest.size () - m_PREFIX_INFO.size () != 4)
     {
       _LOG_DEBUG ("Invalid interest: " << interest);
       return;
@@ -188,8 +188,8 @@
 void
 StateServer::info_actions_file (const Name &interest)
 {
-  if (interest.size () - m_PREFIX_INFO.size () != 4 &&
-      interest.size () - m_PREFIX_INFO.size () != 5)
+  if (interest.size () - m_PREFIX_INFO.size () != 3 &&
+      interest.size () - m_PREFIX_INFO.size () != 4)
     {
       _LOG_DEBUG ("Invalid interest: " << interest);
       return;
@@ -203,7 +203,7 @@
 void
 StateServer::info_actions_fileOrFolder_Execute (const Ccnx::Name &interest, bool isFolder/* = true*/)
 {
-  // <PREFIX_INFO>/"actions"/"folder|file"/<folder|file>/<nonce>/<offset>  get list of all actions
+  // <PREFIX_INFO>/"actions"/"folder|file"/<folder|file>/<offset>  get list of all actions
 
   try
     {
@@ -212,9 +212,9 @@
       /// @todo !!! add security checking
 
       string fileOrFolderName;
-      if (interest.size () - m_PREFIX_INFO.size () == 5)
-        fileOrFolderName = interest.getCompFromBackAsString (2);
-      else // == 4
+      if (interest.size () - m_PREFIX_INFO.size () == 4)
+        fileOrFolderName = interest.getCompFromBackAsString (1);
+      else // == 3
         fileOrFolderName = "";
 /*
  *   {
@@ -234,13 +234,13 @@
       bool more;
       if (isFolder)
         {
-          m_actionLog->LookupActionsInFolderRecursively
+          more = m_actionLog->LookupActionsInFolderRecursively
             (boost::bind (StateServer::formatActionJson, boost::ref(actions), _1, _2, _3),
              fileOrFolderName, offset*10, 10);
         }
       else
         {
-          m_actionLog->LookupActionsForFile
+          more = m_actionLog->LookupActionsForFile
             (boost::bind (StateServer::formatActionJson, boost::ref(actions), _1, _2, _3),
              fileOrFolderName, offset*10, 10);
         }
@@ -249,8 +249,9 @@
 
       if (more)
         {
-          Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
-          json.push_back (Pair ("more", lexical_cast<string> (more)));
+          json.push_back (Pair ("more", lexical_cast<string> (offset + 1)));
+          // Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
+          // json.push_back (Pair ("more", lexical_cast<string> (more)));
         }
 
       ostringstream os;
@@ -324,8 +325,8 @@
 void
 StateServer::info_files_folder (const Ccnx::Name &interest)
 {
-  if (interest.size () - m_PREFIX_INFO.size () != 4 &&
-      interest.size () - m_PREFIX_INFO.size () != 5)
+  if (interest.size () - m_PREFIX_INFO.size () != 3 &&
+      interest.size () - m_PREFIX_INFO.size () != 4)
     {
       _LOG_DEBUG ("Invalid interest: " << interest << ", " << interest.size () - m_PREFIX_INFO.size ());
       return;
@@ -339,7 +340,7 @@
 void
 StateServer::info_files_folder_Execute (const Ccnx::Name &interest)
 {
-  // <PREFIX_INFO>/"filestate"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
+  // <PREFIX_INFO>/"filestate"/"folder"/<one-component-relative-folder-name>/<offset>
   try
     {
       int offset = interest.getCompFromBackAsInt (0);
@@ -347,9 +348,9 @@
       // /// @todo !!! add security checking
 
       string folder;
-      if (interest.size () - m_PREFIX_INFO.size () == 5)
-        folder = interest.getCompFromBackAsString (2);
-      else // == 4
+      if (interest.size () - m_PREFIX_INFO.size () == 4)
+        folder = interest.getCompFromBackAsString (1);
+      else // == 3
         folder = "";
 
 /*
@@ -377,8 +378,9 @@
 
       if (more)
         {
-          Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
-          json.push_back (Pair ("more", lexical_cast<string> (more)));
+          json.push_back (Pair ("more", lexical_cast<string> (offset + 1)));
+          // Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
+          // json.push_back (Pair ("more", lexical_cast<string> (more)));
         }
 
       ostringstream os;
diff --git a/src/state-server.h b/src/state-server.h
index 22caf89..8809b7f 100644
--- a/src/state-server.h
+++ b/src/state-server.h
@@ -50,15 +50,15 @@
  *
  * - state: get list of SyncNodes, their sequence numbers, and forwarding hint (almost the same as RECOVERY interest)
  *
- *   <PREFIX_INFO>/"state"   (nonce should probably be the authentification code or authentication code should in addition somewhere)
+ *   <PREFIX_INFO>/"state"   (@todo: authentification code or authentication code should in addition somewhere)
  *
  * - action
  *
  *   Get list of actions for a folder (for all files under this folder)
  *
- *   <PREFIX_INFO>/"actions"/"folder"/<nonce>/<offset>   (all actions)
+ *   <PREFIX_INFO>/"actions"/"folder"/<offset>   (all actions)
  *   or
- *   <PREFIX_INFO>/"actions"/"folder"/<one-component-relative-file-name>/<nonce>/<offset>
+ *   <PREFIX_INFO>/"actions"/"folder"/<one-component-relative-file-name>/<offset>
  *
  *   Actions are ordered in decreasing order (latest will go first).
  *
@@ -98,15 +98,15 @@
  *      },
  *
  *      // only if there are more actions available
- *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ *      "more": "next segment number"
  *   }
  *
  *
  * - file
  *
- *   <PREFIX_INFO>/"files"/"folder"/<nonce>/<offset>   (full filestate)
+ *   <PREFIX_INFO>/"files"/"folder"/<offset>   (full filestate)
  *   or
- *   <PREFIX_INFO>/"files"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
+ *   <PREFIX_INFO>/"files"/"folder"/<one-component-relative-folder-name>/<offset>
  *
  *   Each Data packets contains a list of up to 100 files.
  *   If more items are available, application data will specify URL for the next packet
@@ -132,7 +132,7 @@
  *      ]
  *
  *      // only if there are more actions available
- *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-FILESTATE>"
+ *      "more": "next segment number"
  *   }
  *
  * Commands available: